ArcObjects 9.3 in Delphi

29
Programming with ESRI’s ArcObjects 9.3 In CodeGear RAD Studio Delphi 2009 Introduction My name is Roger Dunn and I work for the city of Orem, Utah. All of the programmers here have used Borland Delphi for writing database applications since Delphi version 1 or 2. I was hired as the city’s GIS programmer when I didn’t even know what GIS stood for. At the time, our programmers were slowly migrating to Delphi 6 and ArcGIS 8.0 was just about to be released. In the meantime, I learned some Avenue, but concentrated on learning Delphi for the database programming I was also hired to do. In a way, my job consists in bringing our flat IBM databases in sync with our GIS. Since then, ArcGIS and Delphi have both evolved. There is little help in the on-line world for Delphi programmers, including ESRI’s forums, yet I know Delphi to be a pretty popular language. ESRI’s help pages and examples are mainly targeted to users of Microsoft languages such as C++, C#, Visual Basic, and .NET. Hopefully, this document serves as a good springboard for learning to program with ArcObjects in Delphi 2009. It’s taken years to get the skills I’ve got. I don’t want it take years for other programmers to learn to bring these two fantastic technologies together. Preparing ArcGIS 9.3 for Development Since you are a developer, you should install the ArcGIS Developer Resources for VBA Programmers. If you can’t remember whether or not you installed it, just look in your Add/Remove Programs list. It will be listed as ArcGIS Desktop VBA Developer Resources. If you did not install it, then do so from the DVD or Network directory that you originally installed it from. Next, make sure you have the latest patches and Service Packs installed. These can be found at http://support.esri.com in the Downloads section. This link here takes you specifically to Downloads and Updates for ArcGIS 9.3: Patches and Service Packs for ArcView 9.3 . Preparing Delphi 2009 for ArcGIS Development Delphi needs some prepping before you start using ArcObjects. Follow these instructions as best you can.

description

comlementos

Transcript of ArcObjects 9.3 in Delphi

Programming with ESRI's ArcObjects 9.1 in Borland Delphi 7

Programming with ESRIs ArcObjects 9.3InCodeGear RAD Studio Delphi 2009

IntroductionMy name is Roger Dunn and I work for the city of Orem, Utah. All of the programmers here have used Borland Delphi for writing database applications since Delphi version 1 or 2. I was hired as the citys GIS programmer when I didnt even know what GIS stood for. At the time, our programmers were slowly migrating to Delphi 6 and ArcGIS 8.0 was just about to be released. In the meantime, I learned some Avenue, but concentrated on learning Delphi for the database programming I was also hired to do. In a way, my job consists in bringing our flat IBM databases in sync with our GIS.Since then, ArcGIS and Delphi have both evolved. There is little help in the on-line world for Delphi programmers, including ESRIs forums, yet I know Delphi to be a pretty popular language. ESRIs help pages and examples are mainly targeted to users of Microsoft languages such as C++, C#, Visual Basic, and .NET.Hopefully, this document serves as a good springboard for learning to program with ArcObjects in Delphi 2009. Its taken years to get the skills Ive got. I dont want it take years for other programmers to learn to bring these two fantastic technologies together.Preparing ArcGIS 9.3 for DevelopmentSince you are a developer, you should install the ArcGIS Developer Resources for VBA Programmers. If you cant remember whether or not you installed it, just look in your Add/Remove Programs list. It will be listed as ArcGIS Desktop VBA Developer Resources. If you did not install it, then do so from the DVD or Network directory that you originally installed it from.Next, make sure you have the latest patches and Service Packs installed. These can be found at http://support.esri.com in the Downloads section. This link here takes you specifically to Downloads and Updates for ArcGIS 9.3: Patches and Service Packs for ArcView 9.3.Preparing Delphi 2009 for ArcGIS DevelopmentDelphi needs some prepping before you start using ArcObjects. Follow these instructions as best you can.

Get the Latest Delphi 2009 UpdatesDelphi 2009 can be set up so as to automatically detect, download, and install available updates for the software. This one-time option was available when installing Delphi. To check for updates manually, you can: Go to Start > All Programs > CodeGear RAD Studio 2009 > Check for Updates. OR in Delphi 2009's Welcome Page, click Resources > Delphi Updates to see updates in a clickable blog format OR go to http://www.codegear.com/downloads/regusers/delphi to pick, download, and install updates clear back to version 7.Note that the last two options require you to be a registered user on CodeGear's site.

Customize Delphi 2009s Tools MenuThe VBA Developer Kit you installed earlier has some helpful executables that you can run either from the Start menu (under ArcGIS > Developer Tools) or directly from the Tools menu in Delphi. First you'll add the Library Locator. If you already know the coclass, interface, or enumerated type that you need in your code, but you don't know which _TLB to add to your uses clause, then the Library Locator is a great tool to use.

In Delphi, go to Tools > Configure ToolsClick AddFor the Title, type ESRI Library LocatorFor the Program, type C:\Program Files\ArcGIS\DeveloperKit\tools\LibraryLocator.exe. Do not surround the path with double quotes.For the Working dir, type C:\Program Files\ArcGIS\DeveloperKit\tools\. Again, no double quotes.Leave Parameters blank.Click OK.Move the new menu item up or down in the list of Tools, if you so desire.Click Close.In Delphi, click the Tools menu and click ESRI Library Locator to make sure it works.Type IEnvelope in the text box and click Search or hit Enter.Read-only text below the text box will tell you that IEnvelope is in esriGeometry.Pressing the Copy button will copy the string "esriGeometry" to the Windows Clipboard.In your code, you can go to (and make, if necessary) the uses clause in the interface or implementation section and paste the copied string. Then simply add _TLB to the end and Delphi will know where to find the symbol you're using.

Another helpful tool to add to the Tools menu is the ESRI Object Browser. Although the program doesnt provide any textual help as to what certain methods do and what the properties mean, it is a quick tool to find the properties, methods, interfaces, and coclasses by name. Add it to Delphi's Tools menu using the pattern above. The path to the EOB executable is C:\Program Files\ArcGIS\DeveloperKit\tools\EOBrowser.exe. Run the program for the first time and set it up like so:

Go to File > Object Library References.See if there are lots of Libraries that start with ESRI and end with Library. Make sure theyre checked on. You can use the Check All button to speed up the process.If the ESRI libraries are missing, follow these instructions:Click the Add button.Click the Select by file name radio button.Click the Browse button.Navigate to C:\Program Files\ArcGIS\com and select all the files that end with .olb.Click Open.Click OK. The libraries are automatically checked on.Click OK to close the Object Library References dialog box and save your changes.

To test the Object Browser, type IEnvelope in the Search For drop-down box. In the Interfaces group box, verify that Interface Name is checked on (or the "All" checkbox). If only "Property Or Method" is checked, the object browser wont find it. (Also note that when you search, you can select Exact or Contains under the "Search For" box.) Click the Search button. When it finds IEnvelope, double-click the result. IEnvelopes properties and methods will be shown in the bottom panel. By default, the class is shown as it would in VB (see the drop-down box next to the Show Selected Objects button?). Personally, I like it to show as AO Diagram. The Object Browser remembers all of your selections and preferences so you don't have to reset them each time you run it. Even the "Search For" drop-down box will contain all of your most recent searches.Tooltips show a small line of help for the methods and properties. And since IEnvelope inherits from IGeometry, you can expand IGeometry to see its properties and methods. Right-click any interface, method, or property and choose Help. The ArcGIS Developer Help will open up with help on that identifier. The Object Browser wont say what library IEnvelope is in, but the Developer Help will. You could also copy and paste IEnvelope into the Library Locator and find the library name.Now, after all I've said about Delphi 2009's helpful and configurable Tools menu, I personally have experienced many times the loss of these settings. Sometimes I have to re-add the Tool, other times I have to reedit it, and other times I have to reorder it. I don't know what is happening or why, but beware this might happen to you, too.

Receiving ESRI News Feeds from within the Delphi 2009 IDEWhen you open Delphi 2009, you are presented with a Welcome Page. Out of the box, several news feed servers have been installed in the IDE. You access these feeds by going to News > Read News. Your last selected feed will display. To change the feed youre looking at, click the Select Newsfeed link (to the right of the News Headlines) and a menu will appear. Each of these menu items has submenus. You are free to add your own items to these menus, but its not obvious how. These instructions will assist you in adding ESRI feeds to the list of available newsfeeds. With your knowledge of XML, you can change its position in the menu.Using Delphi 2009, open C:\Program Files\CodeGear\RAD Studio\6.0\Welcomepage\xml\menuRSSFeeds.xml. Go to the bottom of the document. Just before the closing tag, insert the following text:

ESRI News Feeds ESRI Main Feed http://www.esri.com/news/rss/rss.xml ESRI Support - Common Issues http://rss.esri.com/support/commonIssues.xml

Save the file, click the Welcome Page tab, and refresh Delphis Welcome Page (the refresh button is a small piece of paper with purple recycling arrows on it). Click the Select Newsfeed link and you should see "ESRI News Feeds" as the last menu item. Hover over it youll see "ESRI Main Feed" and "ESRI Support - Common Issues" as submenus. Click ESRI Main Feed and you should see ESRIs latest headlines with summary paragraphs. To read more, you just click on the headlines link and an ESRI page will load right inside the Delphi IDE! Check out http://www.esri.com/news/rss/news_feed.html for other feed choices. To add a new one to Delphi's Welcome Page, just add another tag to the above tag. The tag will have to have and tags within it. Be advised that, as of this writing, Delphi cannot display RSS feeds from ESRI that contain the query parameter "dotNetBlogs" in them. Such is the case with the ArcObjects blog.Do you want Delphi to download more than just 10 headlines at a time for this and all other news feeds? Open C:\Program Files\CodeGear\Rad Studio\6.0\Welcomepage\xsl\rssFeeds.xsl (you may have to set the filter to Any File (*.*)). Search for the string position. The line has an xsl:if statement that tests for the items position number. Change the 10 to whatever number you like. If you want to see 15 feeds, change it to 15. Save the file, refresh the Welcome Page, and youre set!

Adjust Type Library Import SettingsIn the code blocks below, assume pPoint is of type IPoint. Which code would you rather write?

CODE BLOCK Avar dXValue: Double;begin pPoint.Get_X(dXValue); if dXValue = 0 then begin ... end;end;

CODE BLOCK Bbegin if pPoint.X = 0 then begin ... end;end;

Hopefully your answer was code block B. It gets annoying declaring a variable for each and every object and simple type that gets returned from a function. Its especially tiring when you need a value that is three objects (and three properties) deep and you have to declare 3 variables of the right type in your var section before you can get to that last value.So, under Tools > Options > Environment Options > Delphi Options > Type Library, in the SafeCall function mapping group box, move the radio button from Only dual interfaces to All v-table interfaces. This will make it so that you can code like Block Bs pattern and not Block As. Also checkmark Display updates. This effect is demonstrated on page 12.

Import ArcGIS LibrariesBefore you can code ArcObjects programs and utilities, you need to have Delphi generate .pas files from ArcGIS libraries. Delphi only knows how to resolve identifiers and symbols based on which units are listed in your unit's uses clause. To programmatically convert ArcGIS libraries to .pas files, run the ImportArcGISCOM batch file from a command prompt (which was zipped with this document), being sure to specify your version of Delphi on the command line.You're free to examine the batch file for any preferences you would want to change. The batch file presumes that you want these _TLB.pas files put in the Imports folder for your version of Delphi. It also assumes you want to install ArcGIS components, like TMapControl, in the ActiveX category of your palette. If you make any changes, be sure and save your work before continuing.So, to import files for use in Delphi 2009, type this in a Command Prompt:

ImportArcGISCOM 2009

Create an ArcObjects 9.3 PackageI like having all of ESRIs converted type library files in one package. I suggest you do the same. It puts things together logically.

Open Delphi 2009 and double-click Package in the Delphi Projects group of the Tool Palette. Youll see Package1.bpl in the Project Window.Go into Project Options and click the Description node. In the Description box, type a logical name for the package. This is the string that will show up in the Package List when you go to Component > Install Packages. The Description I gave for my package was ESRI ArcObjects 9.3 COM Objects, but you can put anything you want. If you dont give a description, then the Package List will name your package using the path to the .bpl that gets created when you compile the package. Click OK.Right-click the package, click Add, and select and all files with the pattern "esri*_TLB.pas". For each file you add, two files will be added to the Contains folder: the .pas and .dcr.Compile the package. If you don't get a message about components being added to the palette, right-click to Install the package. Check Delphis Library Environment SettingsWith the Delphi package saved, compiled, and installed, you are just about ready to start programming ArcObjects with Delphi. But you have to make one more check. In Delphi, go to Tools > Options > Environment Options > Delphi Options > Library Win32 and click the Library path ellipses. Make sure that $(BDSUSERDIR)\Imports is in that list. If its not, then add it.As you create ArcObjects components, youll need to include various type libraries in your uses clauses, such as esriArcMapUI_TLB or esriGeometry_TLB. Delphi will only know where to find that file if its in this list of Library Paths.

Starting an ArcObjects Delphi ProjectThis tutorial will show you how to make an object that implements ICommand. The functionality of the object is rather pointless, but its a springboard to creating your own extensions to ArcGIS. As I describe the steps, Ill throw in some comments and opinions about the whole process, as well as some best practices.

Create a Resource File Icons and CursorsBefore I even begin a project, I create an icon for the tool.

Open a graphics program. I use Microsoft Paint (Start > All Programs > Accessories > Paint)Change the graphics size to be 16 by 16 (Image > Attributes)In the color palette, click magenta, or whatever color you want to use as the see-through color. I hate magenta, and its pretty common to use it as a transparent color. Click the Fill tool (the bucket) and fill the white area with magenta.To see the graphic more easily, zoom in on it (in Paint, click the magnifying glass, then click 8x)Per ArcGIS Help, the upper-left pixel must be set to the transparent color. Therefore, leave pixel 0,0 magenta. Other than that, use the color palette and drawing tools to draw anything you want on the icon.As you draw your icon, try to avoid putting non-transparent colors on top of each other so that the disabled version of your bitmap looks nice. For instance, if you have magenta as the transparent color, and a white box with navy blue text on it, then the disabled version of the button will not show the text. The icon will simply be a grey box.When youre done, close and save the bitmap to a new folder under My Documents\RAD Studio\Projects that will eventually contain your Delphi project.

When you create an object that implements ITool, you should also create a Cursor resource, making sure to set a hot spot. As Paint isnt capable of making cursors, youll have to find a tool on the Internet that can.Ive noticed in ArcGIS that the cursors seem limited to four colors: black, white, transparent, and inverse. If you ever have to make a cursor, be sure you use both black and white. If you only use one color and your cursor is over a polygon of that color, your cursor will disappear. So, make a black cursor with a white outline or a white cursor with a black outline, OK?

COM Objects Need a LibraryAny COM objects you create using ArcObjects need an ActiveX library to reside in. Follow these instructions to create the library. Remember, any number of objects can belong to a single library. ESRIs own libraries are a good example.

In Delphi 2009, close any open projects and double-click ActiveX Library in the Delphi Projects | ActiveX section in the Tool Palette. You now have Project1.ridl file open in the Type Library Editor and a Project1.dll open in the Project Manager.Save the project to the folder you created when making the picture.

Using the New Resource Manager in Delphi 2009Putting resources into COM DLL's with Delphi 2007 and earlier was a big pain. But 2009 makes it much easier with the Resource Manager. If you're going to associate custom buttons and cursors with your tools, you'll need to use the Resource Manager.1. With your COM DLL project open in Delphi, go to Project > Resources2. Click the Add button.3. Browse to the 16 x 16 graphic that will be the picture for your custom button in ArcGIS and click OK.4. Select the resource in the list and change its identifier over on the right. Give it a memorable, descriptive name. In the example below, it requires the name of VOWELPIC.5. Click OK.

Make an ObjectYoull now add a custom object to this library. Delphi makes it pretty easy to create objects that implement ArcObjects interfaces, but it also does some extra work that it doesnt need to do. Youll see this when you follow the steps.

1. In Delphi 2009s Tool Palette, double-click COM Object in the Delphi Projects | ActiveX category. The COM Object Wizard shows up.2. Type a name for the class. For this tutorial, call it LayerVowelCounter. Dont start the class name with a T (unless the commands name actually begins with a T, like in TangentMaker, TripodViewWindow, or TrafficAnalyzer).3. Dont leave Description blank and Ill tell you why. If you ever use the Component Category Manager (C:\Program Files\ArcGIS\Bin\categories.exe) to add your object to an ArcObjects category, youll find that the name of your object is blank, simply because you left the Description blank. So, for this tutorial, type Layer Vowel Counter. In the future, you can preface your object with the librarys name. ESRI does that (e.g. esriEditor.CircleTool).4. Leave Threading Model as Apartment and Instancing as Multiple Instance. If you want to know more about these options, consult Delphis Help or the Developers Guide for an in-depth look at these options. [Note: It doesnt matter what you do in the Options group box. Your choices will be overridden once you complete step 7.]5. Youll notice that the Implemented Interface is called ILayerVowelCounter. But we dont want to implement this interface. We want it to implement ICommand. Click the Elipses button and go play 9 holes of golf while the interface list is populated.6. Click the Search box and type ICommand.7. You may have multiple type libraries that declare an ICommand interface, so make sure that the one you eventually select is in esriSystemUI.olb. Click OK.8. Click OK and wait while Delphi not only generates code and a type library for your project, but also regenerates esriSystem_TLB and esriSystemUI_TLB.9. Switch to the Unit1 tab.10. Look at the class source file that gets created and opened for you. The class name will have the standard T placed in front of its name which is why you didnt need it in step 2. Youll also notice its not a TCOMObject, but a TAutoObject. Youll also notice the ICommand interface in the class declaration.11. If you try and compile your project, I just know youll get errors and/or warnings. For instance, youll probably see Declaration of Get_Bitmap differs from declaration in interface ICommand. What? You havent done any coding yet! Delphi did all this for you and it cant even compile what it has generated? Heres the reason for the error and how to fix it: If you were to open esriSystemUI_TLB.pas and went to the declaration of ICommand, the declaration of Get_Bitmap will look exactly like your declaration for your custom command. If you hovered your mouse over OLE_HANDLE in esriSystemUI_TLB, youd find that OLE_HANDLE is declared in esriSystem_TLB. But in your project, it thinks OLE_HANDLE is declared in the ActiveX unit. To fix the error, simply add esriSystem_TLB to your units interface uses clause after the ActiveX unit.12. Compile it again and you might get a bunch of warnings that the return value of all your functions might be undefined. Well, thats because we havent told each function what their results are supposed to be.13. Type the following segments of code in each of the appropriate functions:

FunctionLine of Code

Get_CaptionResult := 'Layer Vowel Counter';

Get_CategoryResult := 'Delphi Tools';

Get_CheckedResult := False;

Get_HelpContextIDResult := 0;

Get_HelpFileResult := '';

Get_MessageResult := 'Click this command to count the number of vowels in all the layers of this map.';

Get_NameResult := 'DelphiTools_LayerVowelCounter';

Get_TooltipResult := 'Counts vowels in layer names';

14. For the Get_Bitmap function, youll need to store a handle to a bitmap resource. Declare a private variable of type HBITMAP in your class. Suppose we call it hVowelIcon. The Get_Bitmap function would read

if hVowelIcon = 0 then hVowelIcon := LoadBitmap( HInstance, VOWELPIC );Result := hVowelIcon;

15. VOWELPIC is the name of the Bitmap resource you added to the Resource Manager on page 8.16. To make sure this handle gets freed when your object is freed, override the class destructor. Call DeleteObject( hVowelIcon ); in that procedure.

There are three more functions to implement, but they take some more preparation: Get_Enabled, OnClick, and OnCreate. But I better explain what this tool is going to do before I show off the code. When this command is clicked, a message box will show a count of how many vowels show up in the names of all the layers on the focus map. I know its stupid, but remember that the purpose of this is to help you use ArcObjects in Delphi.

17. The OnCreate function gives us a Hook into the application. To use it in the other functions, well need to save it somewhere. The best place is in a private member of the class. In your class declaration, declare a private variable called pMxApp of type IMxApplication.18. While youre at the top of your code, add esriArcMapUI_TLB to your uses clause. That way, Delphi will know where IMxApplication is declared. esriArcMapUI_TLB.pas was created when you imported it several pages ago.19. In the OnCreate function, type Hook.QueryInterface( IMxApplication, pMxApp); You might think you could accomplish the same thing by typing pMxApp := Hook as IMxApplication; However, the Delphi Help says that the as operator will throw an exception if the object doesnt implement the specified interface. Therefore, if someone adds your command to their ArcCatalog toolbar, your command would crash ArcCatalog ungracefully. In that case, Hook would implement IGxApplication, not IMxApplication. Therefore, use the QueryInterface function for safe QIs. Check out the section below entitled Query Interfacing.20. The Get_Enabled function uses the pMxApp variable to determine if the command should be enabled. We not only want it available in ArcMap, but only when there are one or more layers in the focus map. So, use this code for the Get_Enabled function:

var pMxDoc: IMxDocument;begin if pMxApp = nil then Result := False else begin (pMxApp as IApplication).Document.QueryInterface( IMxDocument, pMxDoc ); //Since pMxApp is of IMxApplication, we can guarantee //that Document is of type IMxDocument. Result := pMxDoc.FocusMap.LayerCount > 0; end;end;

The code will not compile right now because Delphi doesnt know what IApplication is. If you use the ESRI Library Locator, youll find that IApplication is defined in esriFramework, so add esriFramework_TLB to your uses clause. Personally, I would add it to the implementation sections uses clause.

21. The OnClick function includes some of the code from the Get_Enabled function. Use this code as the entire function:

var pMxDoc: IMxDocument; pFocusMap: IMap; iVowelCount, I, J: Integer; wLayerName: WideString;begin if pMxApp nil then begin iVowelCount := 0; (pMxApp as IApplication).Document.QueryInterface( IMxDocument, pMxDoc ); pFocusMap := pMxDoc.FocusMap; for I := 0 to pFocusMap.LayerCount - 1 do begin wLayerName := WideUpperCase(pFocusMap.Layer[ I ].Name); for J := 1 to Length( wLayerName ) do case wLayerName[ J ] of 'A', 'E', 'I', 'O', 'U': Inc( iVowelCount ); end; end; ShowMessage( 'There are ' + IntToStr( iVowelCount ) + ' vowels in all of these layer names.' ); end;

The code will not compile because Delphi doesnt know where to find some of the common Delphi functions. COM Libraries and object units arent automatically generated with all the same units that are generated for forms executables in Delphi. So, manually add Dialogs and SysUtils to your uses clause. IMap wont resolve until you add esriCarto_TLB to the uses clause. Remember, I find these units by using the Library Locator.If you compile at this time, you should get no errors and no warnings. Note that if you ever try and compile an ArcObjects DLL that ArcGIS is using, youll get a warning that it couldnt be created. Close down ArcGIS, recompile, and start the program again.

Add Your Command to ArcMapThe following steps are covered adequately in one or more ArcGIS Helps and Tutorials. They are no different whether youre adding a DLL compiled in C#, VB, or .NET technologies.Open ArcMap and go to Tools > Customize.Click on the Commands tab.If you have a map document open, you have the choice of adding your command to the Normal.mxt template or to the currently open map document. Make that choice in the Save in drop-down box.Click Add from fileBrowse to your Delphi project folder where your .dll compiled to. Youll also see your type library there (.tlb), but select the DLL and click Open.An Added Objects dialog will appear with the names of any and all COM objects in the DLL. Note that this box is read-only and is only there as an FYI.Click OK.The Customize Dialog box will zoom to the category that was programmatically specified by the command (ICommand.Get_Category). Go ahead and drag-and-drop the Vowel Counter command to some toolbar.

Test Your CommandIf there are no layers in your map document, hopefully your new command is disabled. Add one or more layers to your map and see if the command enables. Now click it. You should get a dialog box that has already counted the number of vowels in all of the layer names in your map. Hover over it to see what you wrote for the Get_Tooltip function. While still hovering over your command, look at the status bar and see what you wrote for the Get_Message function. If you Customize ArcMaps toolbars, right-click your command, and select Image & Text from the context menu, youll see what you wrote for the Get_Caption function. Youll also see that text if you add your command to any menu, as menus will always show the Caption.

DebuggingDebugging an ArcObjects DLL built with Delphi 2005 or later is easy. In the Project > Options dialog, click the Debugger node. In the Host Application box, type, or browse to, C:\Program Files\ArcGIS\Bin\ArcMap.exe. Click OK to save the options. Put a breakpoint in the OnClick procedure. Click Run (or Run menu > Run). At first, the breakpoint will get an X in it. It will become red again once ArcMap has fully started. Add a layer and click your Layer Vowel Counter command. Youre now stepping through your code in the IDE!Be careful, though. Dont put breakpoints in a method thats called a lot, such as the Get_Enabled function. ArcMap constantly calls this, and other functions, when its idle. It has to! It doesnt know if a given tool is waiting for a selection to be made, an element to be put on the layout, or the map to be given a rotation angleso it keeps asking every command if its enabled.

Set Up Your Map Document For Further TestingWhen you really get into creating ArcObjects DLLs, theyll get much more complicated than this. Therefore, I suggest that before you program future objects for ArcMap, set up ArcMaps window size, open a good test document (or make one), and make all the settings you want to persist on this map. Create a selection, zoom to where you want to be, set layer properties, etc. Then, save and close the map. Then create an easily accessible shortcut to it (like in the QuickLaunch menu). Do this so you can always start ArcMap in a mode to quickly test your DLL. If you dont set up a map this way, youll find yourself doing many repetitive tasks just to get your map in a state for testing your DLL each time youve changed the code.Only then should you reopen ArcMap and try out your new DLL. If it crashes ArcMap, you at least have your map document saved and ready to try again. There have been so many times where I have recreated the same map document because I didnt save it and I wanted to test my DLL, thinking it would be really quick or just a small test.

Using Delphis Type Library EditorWith your ArcObjects project open and active in Delphi, go to View > Type Library. Make the top-most element of the tree active. That node represents the library. To the right you can see properties of the library. The tree itself contains any objects, interfaces, enumerations, etc. that you have added to the library.If you click on the Uses tab, youll see a reference to the ESRI System UI Object Library, as well as its version and path. That was automatically put there when we created an object that implemented ICommand.The Type Library editor has buttons across the top and context menus to add items to your DLL. Please know that if you create a CoClass from within the Type Library editor, no code will be generated for you. CoClasses must be made from the COM Object Wizard to produce automatically-generated code like function stubs.Delphi has another option that keeps the Type Library and Code Editor windows in sync. If you go to Tools > Options > Environment Options > Delphi Options > Type Library, youll see a checkbox labeled Display updates. When that box is checked, changes you make in the Type Library window will not happen until youve clicked an OK button. When the box is unchecked, those changes happen without any approval from you. Please check that box to follow the little walk-through below.

Implementing Multiple InterfacesAs this tutorial was a simple example, I chose to make an object that only implemented ICommand. The fact is that many objects in ArcGIS implement multiple interfaces. Objects that implement ITool, for instance, have to implement ICommand as well. When you create a COM object with Delphis COM Object Wizard, you dont get the choice to specify multiple interfaces to implement. This is where the interactive Type Library window comes into play.

In your Delphi project, view the Type Library.Click on the LayerVowelCounter CoClass.Click on the Implements tab.Right-click the right-hand window area and choose Insert Interface.From the list of interfaces, choose ITool and click OK.If you have Auto-Refresh checked, simply click the Unit1 tab and you will be presented with the Implementation File Update Wizard. If Auto-Refresh is unchecked, click the Refresh Implementation button (a piece of paper with the recycling symbol on it).The Implementation file update wizard shows changes that will be made to your code when you click OK. There are 11 suggested changes. If you hover your mouse over each change, you can see details of the method-to-be. Note that you can check on or off any of these changes and you can also opt not to show that dialog again. If you uncheck it and you didnt mean to, you can turn it on under Tools > Options > Environment Options > Delphi Options > Type Library > Display updates checkbox.For purposes of this tutorial, click Cancel.Right-click the ITool interface and select Remove Interface. This exercise was just to show you how to make objects implement multiple interfaces.

[The steps above worked fine in Delphi 2007, but I found that in the 2009 version the Display Updates option does nothing, and auto-refreshing the code from the Type Library doesn't work either. I had to manually hit the Refresh button in the Type Library Editor to get it to update the code in Unit1. But it still didn't show me any of the pending changes]

Anyway, in the steps just above, you might have noticed that the list of interfaces was quite short. Thats because it only listed interfaces from esriSystemUI.olb. If you need to create an object that implements an interface in a different library, you would follow these steps:

1. Use the ESRI Library Locator (hopefully in your Tools menu by now) to find out what library the desired interface is in. Or perhaps you know the library name because you found the interface using ESRIs help.2. In the Type Library editor, youd click on the node that represents the library itself. It will be the parent node. Its icon is three blue diamonds.3. Click the Uses tab4. Right-click the right-hand window area and choose Show All Type Libraries. All the registered type libraries on your system will be listed in alphabetical order.5. Scroll down to an ESRI library by clicking any library, and typing ESRI, without the quotes.6. Click the checkbox next to the library that has the interface you were looking for in step 1.7. Right-click the right-hand window area again and choose Show Selected.8. Click the Refresh Implementation command.9. Now, when you wish to add an interface to the Implements tab of a coclass, the resulting dialog will contain all interfaces defined in the libraries you had checked.

GUIDsEverything you create in the Type Library editor, such as your own interfaces or CoClasses, gets a GUID (Global Unique Identifier). Even your library has its own GUID. GUIDs can be seen in the Type Library editor under the Attributes tab for whatever item you click on in the left-hand window. Of course, some things dont have GUIDs, such as functions, properties, and enumeration constants.If you ever need to generate a GUID in the code window, press Ctrl+Shift+G. Globally unique identifiers, when not messed with, will uniquely identify your object to every computer in the world. Not that every computer in the world will know about your object, but whatever machine your object is registered on will be uniquely identified by that hexadecimal string.

VBA to Delphi ConversionConversion TableThis table should help you take code that you see in VB(A) and convert it to Delphi. This table presumes you can concatenate in Visual Basic or Delphi by yourself. By knowing how to translate between these two languages, all the ArcObjects coding examples are useful to you, as well as the forums. I even needed help once so bad that I translated all my code into VB, got help in VB, and translated the answer back into Delphi.Know that Visual Basic will QueryInterface variables automatically, but in Delphi you have to cast the results into the desired interface. Delphi is more strictly typed than Visual Basic. Sometimes you have to QI a variable that is the result of a function.Most importantly, you have to know that some methods and properties in ArcObjects use Delphi keywords, so Delphi changes those method names so there are no conflicts. To see this for yourself, open up esriCarto_TLB.pas in Delphi 2009 and youll see a whole smattering of comments dedicated to name changes. They are labeled as Hints. Some of them arent consequential as they just change the parameter names for some functions. But you can see that the IAnnotateLayerProperties.Class property changes to IAnnotateLayerProperties.Class_ because class is a Delphi keyword. ILegendClass.Label is changed to ILegendClass.Label_ for the same reason.

VB(A) CodeDelphi Code

Dim m_pEnveLope As IEnvelopeDim pFeature As IFeatureSet m_pEnvelope = New EnvelopeDim m_pPoint = New Pointvar m_pEnvelope: IEnvelope; pFeature: IFeature; m_pPoint: IPoint;begin m_pEnvelope := CoEnvelope.Create as IEnvelope; m_pPoint := CoPoint.Create as IPoint;

Set pFeature = pEnumFeat.NextpFeature := pEnumFeat.Next;

MsgBox pApp.CaptionShowMessage( pApp.Caption );

pApp.Visible = not pApp.VisiblepApp.Visible := not pApp.Visible;

Set pPoint = pEnumGeometry.Nextvar pGeometry: IGeometry; pPoint: IPoint;...begin... //IPoint inherits from IGeometry pGeometry := pEnumGeometry.Next; Supports( pGeometry, IPoint, pPoint );

While Not pFeature Is Nothingwhile pFeature nil do

Dim pApp as IApplicationDim pEditor as IEditorDim pID as New UIDpID = "esriEditor.Editor"

Application is a global variableSet pApp = ApplicationSet pEditor = pApp.FindExtensionByCLSID(pID)Var //pApp would be a private variable of your class. pExt: IExtension; pEditor: IEditor; pID: IUID;begin pID := CoUID.Create; pID.Value := GUIDToString( esriEditor.CLASS_Editor ); //pApp assigned during OnCreate or something pExt := pApp.FindExtensionByCLSID( pID ); Supports( pExt, IEditor, pEditor );

Other ArcObjects-Related Delphi Programming TipsYears of experience and hours of debugging have helped me compile this list of useful tips when programming with ArcObjects within Delphi.

QueryInterfacing (QI)As Ive stated earlier in this document, the QueryInterface method is a good one to use when you have an object and need to know if it implements a certain interface. What I didnt state was that it returns an HRESULT. You can find a list of HRESULTs in Delphi under Help. Use the Helps Index tab to find HRESULT values [Security]. Essentially, bad HRESULTs start with E_ (Error) and the good HRESULT starts with S_ (success).

SupportsSometimes youd like to test if the object youve got is nil before you call QueryInterface on it. Whenever you dare to call a method on a nil object at run-time, it results in a serious exception. So, Delphi provides a neat method called Supports, which is in the SysUtils unit. Supports checks if the object is nil, returns an out pointer if the incoming object supports the specified interface, and returns a simple boolean if it was a success. Delphi help has more on this topic. Whereas QueryInterface is a method of IUnknown, Supports is a stand-alone method.You should also know that you cannot use standard type-casting syntax to change from one interface type to another. For instance, you have probably written code like this in an Action event handler: TAction(Sender).Enabled := False; But I warn you not to type-cast interfaces like that. If you write IFeature(pCursor.NextRow); where NextRow returns an IRow that could really be QId into an IFeature, the code will crash. Properly turn the value of NextRow into an IFeature by using QueryInterface or Supports.

Error HandlingTry...except and Try...finally blocks can be a life-saver in your functions. Because youre working with objects, interfaces, and variants that are assigned at run-time by ArcMap, you should try to be prepared for anything. Write code thats pertinent to what you want to do, but also write code to handle exceptions. Unhandled exceptions can have an indeterminate effect in ArcGIS. For instance, if your Get_Enabled function throws an unhandled exception, ArcGIS will Enable the button! I learned that the hard way.All of your COM methods have the SafeCall calling convention. That means that Delphi secretly generates code around your functions to catch exceptions and translate them into COM exceptions, sending the HRESULT to ArcGIS.Youll recall a little paragraph several pages ago that mentioned you need to check on a little Environment setting called SafeCall function mapping wherein we chose All v-table interfaces. The default option, Only dual interfaces, will throw unhandled exceptions to ArcMap which usually results in ArcMap crashing while it throws up an exception address resembling $beadfeed.

Overriding the Constructor and DestructorWhether you know it or not, ArcGIS is calling CoCreate on your objects and using the properties you built. For instance, on the tutorial object you created, it calls CoLayerVowelCounter.CoCreate, casts it as an ICommand, then reads the bitmap handle and copies the bitmap to a button face. It reads your Enabled property, Message, Tooltip, and other such properties and updates its user interface to reflect these values.Because COM objects are created differently than Delphi TObjects, do not override the Create constructor to perform member initializations. Rather, override the Initialize procedure instead. Initialize takes no parameters and is public. However, you can still override the Destroy destructor to free objects or make other unassignments when your object gets destroyed.As in all of COM, your object gets destroyed when a variable pointing to it is set to nil, or if your object goes out of scope. Free is never called on your object from ArcGIS.

Calls to Your DLL, Instancing, and ThreadingFirst off, your Wizard-created COM library and associated objects are called in-process COM servers. This means that every ArcGIS application will create a copy of your DLL and attach it at run-time to the application. ArcMap, for instance, is a client of your custom objects. One of the ways you know this is because of the OnCreate procedure that sends you a hook to the application. That hook is a window handle to ArcMap, ArcCatalog, ArcScene, etc. You only get one hook. Therefore, all of the calls made to your objects or global variables will be in one running instance of an ArcGIS program.As you program using ArcObjects in Delphi, you may come across instances where you need to access global objects such as counters, interface pointers, components, or forms. To access these successfully, youll need to know how ArcGIS makes calls to your DLL, so Ill teach you that. To start, look at the bottom of your objects unit and youll see a line similar to the following:

initialization TAutoObjectFactory.Create(ComServer, TLayerVowelCounter, Class_LayerVowelCounter, ciMultiInstance, tmApartment);

Youll notice these parameters: ComServer (the global ComServer object), TLayerVowelCounter (your custom objects class), and the Class_LayerVowelCounter constant (a GUID found in your Type Library and declared in your type librarys .pas file). The next two parameters were options you chose when you created your object. Look at Delphi Help under TAutoObjectFactory, Create for more of an explanation for those parameters. But heres how the above two default values affect VCL Forms in your DLL.In the line of code above, the ciMultiInstance parameter means that for every one of your objects that ArcGIS creates, it CoCreates a separate instance of your object. What Ive found, though, is if ArcGIS has my command on a menu and on various toolbars, my ICommand object is only created once. The ciMultiInstance parameter merely says that if my class is cocreated twice, it gets two objects, each with its own values to private variables and such. If TAutoObjectFactory.Create was called with the ciSingleInstance parameter, each of those two calls to CoCreate would result in returning the same object.The tmApartment parameter represents the threading model. Basically, it ensures that all local variables to your class are reliable across multiple calls to your objects functions. You are guaranteed that when any of your objects functions are running, none of the other functions in your methods is being run at the same time. It is possible, however, that functions in your other objects (in the same library) may be receiving function calls at the same time as the current object. If ArcGIS has CoCreated two of your objects that reside in the same DLL, then two of THOSE functions COULD run at the same time, but theyll be using their respective instances variables. I knowits hard to understand. But knowing this stuff becomes important as your DLL gets more complicated.Remember that the parameters to TAutoObjectFactory.Create are generated for you when you make your selections in the COM Object Wizard. You can always change their values in the resulting unit. See Delphis help under TAutoObjectFactory, Create for more options and what they mean.

Global VariablesOne good place to assign global variables and create global objects is in your units initialization and finalization sections. However, remember that you cannot do any class member initializations because instances of your class are created by ArcGIS at run-time. This means you cant create a form if the only variable pointing to that form is a member of your class.If you access global variables and objects, you must write code to read and write these values in an appropriate manner. You may have to write critical sections when you change the value of a global variable, or ensure that a pointer is still valid before making a function call on it.Things get more complicated if you want to share a global variable across instances of ArcGIS programs. For instance, suppose you wanted a command to count the number of layers in all the open instances of ArcMap. Although Ive never done this, I do have a few ideas.

1. In Delphi 2009, double-click Automation Object in the "Delphi Projects | ActiveX" category of the component palette.2. Specify a CoClass Name.3. Choose the Threading Model to be Apartment. Lets keep it simple so we dont have to worry about thread support. This component is light-weight anyway, so we wont take up a lot of time executing.4. Choose Instancing to be Single Instance.5. We can leave Generate Event support code off. After all, well wait for the user to press a button before tallying the layerswe dont need to listen to when the layers are actually added and removed.6. In the resulting Type Library, add ESRI SystemUI Object Library to the Uses tab.7. Remove the automatically-generated interface.8. Add ICommand to the objects Implements tab.9. Refresh the implementation.10. Set the properties for ICommand, such as Bitmap, Enabled, etc. Add appropriate esri*_TLBs to your uses clauses whenever the compiler complains or when Code Insight cant help you.11. Add a private variable that is a TInterfaceList of IMxApplications.12. Override the Initialize procedure to create the list.13. Override the Destroy destructor to free the list.14. In the OnCreate handler, take the hook parameter and add it to the dynamic array. Remember, since this class is single-instance and we chose the Apartment threading model, our class private members are safe across multiple calls to this DLL.15. In the OnClick procedure, loop through the array of IMxApplications and try to access their documents. Then, count the number of layers in their FocusMaps, adding them up. Remember that the user may have closed one of the ArcMaps already, so perhaps some of the IMxApplication handles arent valid anymore. Use try...except handlers. Your object should still recover and count the layers in the maps that still do exist.

Remember, these are just ideas. I dont know if they would work. But I do know that out-of-process COM servers do need to be executables. You should probably keep the automatically-generated form, as that may be where you want to display your results.

Delphi FormsUsing Delphi Forms in a DLL can be tricky and the final code will depend on the purpose and scope of each form. Do each of your custom objects in the DLL have their own forms? Do all of your custom objects access the same instance of the form? Do they each create their own instances of the same form class? Are the forms modal or modeless? Since this document cannot address every situation, I describe here some best-practice locations to create and free forms from within your DLL.Every descendent of TCustomForm needs a TComponent as the parameter to the Create function. One valid parameter to use is the global Application variable. To accomplish this within an ArcObjects DLL, youll have to add Forms to your uses clause. At run time, the Application variable will have a Handle of 0, which is a problem when drawing windows. Therefore, you should assign the Application.Handle in the OnCreate procedure like so:

var pApp: IApplication; ...begin ... Hook.QueryInterface( IApplication, pApp ); if ( pApp nil ) and ( Application.Handle = 0 ) then Application.Handle = pApp.hWnd;

Note that when youre programming an executable in Delphi, you should never assign the Handle property. But the Delphi Help says to do so in a DLL, so do it.Another way to Create a form at run-time is to call the CreateParented method of the form. It takes a HWND rather than a TComponent. ArcGIS IApplication interface has a suitable Handle parameter that you can send to the CreateParented function.With regards to the code above, note carefully what ArcObjects help says about the OnCreate function:

When you implement ICommand to create a custom command, you will find that your class constructor and destructor are called more than once per session. Commands are constructed once initially to get information about them, like the name, bitmap, etc and then they are destroyed. When the final, complete construction takes place, the OnCreate method gets called. OnCreate gets called only once, so you can rely on it to perform initialization of member variables. You can check for initialized member variables in the class destructor to find out if OnCreate has been called previously.

This means that your COM class Initialize and Destroy procedures are called multiple times before OnCreate is. If you decide to put code in Initialize and Destroy to create and destroy forms, then only do so if OnCreate has been called. The best test is to see if your private IApplication (or IMxApplication or IGxApplication) variable is nil. If it is nil, then dont create the form in Intialize and dont destroy it in Destroy. However, if its been assigned, then do create it in Initialize and destroy it in Destroy.According to ESRIs Help, it seems best to create forms in the OnCreate function. Tear them down in Destroy, but only after testing if theyve been created or not.You may also end up programming a dockable/floating window object which implements IDockableWindowDef. Luckily this interface also has an OnCreate procedure that gives you a hook to the hosting application. When creating a dockable window for ArcGIS, implement IDockableWindowDef (not IDockableWindow), Create the form in OnCreate, and destroy it in Destroy, but only if your local IApplication variable is not nil. This interface has a Get_ChildHWND function which requires you to return an OLE_HANDLE. Simply return the Handle property of your form.

OleVariantsOleVariants are commonly needed and returned in methods dealing with field values. The Value property of the IFields interface is an array which requires the index of a field. You acquire the index of the field youre looking for with FindField. If the result is -1, do not call the Value property to access that fields value else ArcGIS will throw an exception. Anyway, field values are always set and returned as OleVariants.The most important lesson Ive learned is that if you need to concatenate an OleVariant string to a Delphi string, you just cant use the + operator. For instance, the following code will crash:

var pFeatureCursor: IFeatureCursor; pFeature: IFeature; iAddrFieldIndex: Integer; oleAddress: OleVariant; ...begin ... { Assign pFeatureCursor appropriately. } iAddrFieldIndex := pFeatureCursor.FindField('ADDRESS'); pFeature := pFeatureCursor.NextFeature; while pFeature nil do begin oleAddress := pFeature.Value[iAddrFieldIndex]; ShowMessage('This feature''s address is ' + oleAddress); pFeature := pFeatureCursor.NextFeature; end; ...end;

Do you see the plus sign in the call to MessageBox? The problem is that Delphi will attempt to add oleAddress to the value of the fixed string, rather than concatenate it. The error will be that it couldnt covert Variant of type (String) to type (Double). The way to fix it is to use the VarToStr function:

MessageBox('This feature''s address is ' + VarToStr(oleAddress));

OleVariants obtained through field values are also capable of holding null values. The VarToStr and VarAsType functions will convert a null value to some standard value. But if you use an OleVariant in a calculation, and its not assigned, your DLL will crash. To see if an OleVariant is holding a null value, call the VarIsNull function, and pass it the OleVariant. It will return True if the variant is null. VarIsEmpty is also sometimes useful as it sees if the Variant has even been assigned a value. Note how odd it is that Null is considered a value when dealing with variants. We learned long ago that Null isnt the same as 0 or the empty string because those are at least something. But with Variants, even Null is something. Weird. AnywayAnother thing Ive noticed with ArcObjects and OleVariants is what happens when you grab the value of an empty String field. Instead of getting , as youd expect, youll actually get a space . Therefore, it may help to call the Trim function on your result to wipe out the space. You can then compare it to the empty string and do whatever you were planning to do in the case of empty strings.

Outbound Interfaces (Event Handling)Outbound interfaces are used when ArcGIS sends a message to lots of objects that have a listening ear. These messages are also known as events. For instance, when the Selection object in ArcCatalog has changed, it fires an OnSelectionChanged event to all objects which implement the IGxSelectionEvents interface and have registered to listen to that event.In the Visual Basic examples, youll see code like this:

Private WithEvents pGxSelection as GxSelection

In Delphi, its not as simple. First, your object declaration must specify that its implementing a specific outbound interface. If your object is already created and coded up, then use the Type Library editor, as described on page 12, to add an interface like IGxSelectionEvents to your Implemented Interfaces list. Then refresh the implementation so Delphi creates function stubs for you.Second, you have to declare two (preferably private) variables: one of type IInterface and one of type LongInt. The IInterface variable will always be pointing to the ArcGIS object that your object is listening to. The LongInt is a sort of handle representing your listening connection to ArcGIS.Third, in an appropriate method of your object, you must declare that you are an object listening to an ArcGIS object. By appropriate method, I mean one thats going to be called once by ArcGIS, such as OnCreate or Activate. (See Overriding the Create Constructor, above, for more ideas). Assign the IInterface variable to an object in ArcGIS thats going to be sending you events. Continuing with the IGxSelectionEvents example, youll need to listen to the GxSelection object. Obtain a reference to the object through IGxCatalog.Selection and youll get an IGxSelection pointer. Cast it to an IInterface pointer and store it to the IInterface variable you created.To declare that you are a listener, call the InterfaceConnect method (declared in the ComObj unit). The first parameter to this function is the IInterface variable. The second parameter is the name of the Interface that you are listening to, such as ISelectionEvents. The third parameter is Self as IInterface (meaning that this object, Self, is to be counted as listening), and the fourth parameter is the LongInt variable.Fourth, in an appropriate method of your object, you must stop listening for the event. By appropriate method, I mean one that destroys your object, or tells your object its going to be destroyed. You then call InterfaceDisconnect. The first parameter to this method is the IInterface variable as before. The second parameter is the name of the interface you were listening to, like IGxSelectionEvents. The third parameter is the LongInt variable.

Array Parameters to ArcObjects FunctionsSome ArcObjects methods require or return arrays. The ISelectionSet interface, for example, has two methods, AddList and RemoveList, that take arrays. The parameter is called OIDList and, interestingly enough, is a Long integer type. I used to think it wanted a pointer or an address. I read a lot in Delphis Help and severely tested Variant arrays and COMs SafeArrays, but the solution ended up being much simpler than these. All you do is declare a dynamic array variable of type Integer. You initialize, grow, and shrink it like you would any other dynamic array (SetLength, High, Low, [ ], etc.) But this is how you pass it to a call to RemoveList:

pSelectionSet.RemoveList( Length( aMyArray ), aMyArray[ 0 ] );

In other words, you send it a length and a reference to the first integer. Since the OIDList parameter is not passed by value, that means its being passed by reference. Still, passing aMyArray all by itself will not yield a correct result. You have to reference its first element (0).Reading an array thats passed to you from ArcObjects is just as simple. Simply access the elements with brackets. You dont need to free those kinds of arrays because you, too, are reading memory allocated by ArcObjects.

Automatically Registering Objects in Component CategoriesWhen you manually add a DLL to the ArcMap list of commands, ArcGIS registers the DLL and puts all objects that implement ICommand into the ESRI Mx Commands category. You can find out more about categories on EDN.In Delphi, you can have your DLL automatically register your components into the categories that you want them registered. Conversely, when your DLL is unregistered, it will remove itself from those same categories. This will occur whether the user uses regsvr32 at a command prompt, registers it from the Windows Explorer context menu, or if you have InstallShield auto-register the DLL when its installed to your users machines. [Remember that you can also register and unregister your DLL right from within Delphi. These two commands are in the Run menu.]To auto-register your objects in multiple ArcGIS categories, download the following ArcScript: http://arcscripts.esri.com/details.asp?dbid=14207 . Save this file to a .pas file and include it in each one of your Delphi ArcObjects projects. This code was written and submitted by Berend Veldkamp. His tips have been invaluable in creating this document.In each project, go to Project > View Source. Follow the code pattern established by Berend at http://forums.esri.com/Thread.asp?c=93&f=1170&t=165456#486840 . Essentially, you redeclare the DllRegisterServer and DllUnregisterServer functions in your projects main source file. Pay attention to these tips when following Berends pattern: The DllRegisterServer and DllUnregisterServer functions are case-sensitive. They return HRESULTs and are declared with the stdcall calling convention. When calling these overloaded functions in your own functions of the same name, preface them with ComServ., including the period. This will let the regular functions do their thing and prevent an infinite loop. In DllRegisterServer, call the ComServ version of the function BEFORE adding your component to the desired categories. Assign the result of that function to your functions Result variable. In DllUnregisterServer, call the ComServ version of the function AFTER removing your component from the desired categories. Assign the result of that function to your functions Result variable.

You can always add more category constants to the unit, such as c_EsriGxCommands ({5F08CBCA-E91F-11D1-AEE8-080009EC734B}), c_EsriGxViews ({3EEA2CB1-A730-11D2-AF6D-080009EC734B}), and c_EsriGxExtensions ({4531C69D-DC07-11D2-9F2F-00C04F6BC69E}).The best way to find component categories is to look up the interfaces you are implementing on EDN. Generally the help pages will specify the categories your components should be in. If thats not good enough, you can find all ESRI Category UIDs in the registry under HKEY_CLASSES_ROOT\Component Categories. Keep in mind that other software manufacturers use categories, so you may want to go to this key and then use the Find command to zoom to a category. When you find one, its UID is the name of the open folder node in the tree.

General Delphi Programming Tips

Use TStringLists for LoggingShowMessages are one way of displaying run-time information to you as you try and debug your DLLs from ArcGIS. However, these may get annoying, especially if youre in an infinite loop or youve already decided to stop testing a certain block of code, but forgot to take out the Message Box. That box would also annoy users in a production environment.So one thing I do is create TStringLists and use the Add method to add logging messages. I also use the SaveToFile method in exception handlers and destructors. I can then open the file repeatedly with a shortcut to see a log of all that went wrong (or what went right). In a production environment, a log can be kind of handy when youre trying to debug a problem on someone elses machine. You want to know what sections of code have executed correctly and what method ArcGIS crashed in. The user has done something for which you havent properly accounted for, and a log file can be a good indicator of where to start debugging.Log files, obviously enough, are easy read over the phone by users, or sent by e-mail for further detailed investigation. I personally dont see anything wrong with an ICommand or ITool logging all operations by the user.

Useful Delphi 2009 Help pagesReading Delphis COM documentation will give you more insight into programming with ArcObjects since it is built on the COM infrastructure. To find Borlands documentation, open Help and activate the Contents tab. Expand RAD Studio > RAD Studio (Win32) > Reference > Win32 Developers Guide > Developing COM-based Applications. I suggest two files right off the bat: Creating COM clients > Code Generated When You Import Type Library Information Using ActiveX controls > How Delphi Adds Properties Working with type libraries > Adding a(n) * to the Type Library

Public Comments and Updates to This DocumentFuture versions of this document are available where you originally got it, on ArcScripts. More specifically, this document is available at http://arcscripts.esri.com/details.asp?dbid=14204 . You may also e-mail me directly from the Downloads page. Public comments are available for this, and past, versions of the document on the ESRI forums at http://forums.esri.com/Thread.asp?c=93&f=1170&t=165456&mc=6 . You can also choose to watch the thread so that any comments to the document, or update notifications, will show up in your inbox!