arc objects 9.3 in delphi 7

27
Programming with ESRI’s ArcObjects 9.3 In Borland Delphi 7 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. Although Borland has released Delphi 2005 with .NET ability, I haven’t yet learned it. Hopefully, this document serves as a good springboard for learning to program with ArcObjects in Delphi 7. 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 7 for ArcGIS Development Delphi needs some prepping before you start using ArcObjects. Follow these instructions as best you can.

Upload: -

Post on 14-Oct-2014

134 views

Category:

Documents


3 download

TRANSCRIPT

Page 1: Arc Objects 9.3 in Delphi 7

Programming withESRI’s ArcObjects 9.3

InBorland Delphi 7

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 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. Although Borland has released Delphi 2005 with .NET ability, I haven’t yet learned it.

Hopefully, this document serves as a good springboard for learning to program with ArcObjects in Delphi 7. 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 DevelopmentSince 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 7 for ArcGIS DevelopmentDelphi needs some prepping before you start using ArcObjects. Follow these

instructions as best you can.

Get the Latest Delphi 7 UpdateTo check for updates on the Delphi 2006 product, go to

http://www.codegear.com/downloads/regusers/delphi to pick, download, and install updates clear back to version 7. Note that you need to be a registered user on CodeGear's site to download anything.

Customize Delphi 7’s 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

Page 2: Arc Objects 9.3 in Delphi 7

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.

1. In Delphi, go to Tools > Configure Tools…2. Click Add…3. For the Title, type ESRI Library Locator4. For the Program, type C:\Program Files\ArcGIS\DeveloperKit\tools\

LibraryLocator.exe. Do not surround the path with double quotes.5. For the Working dir, type C:\Program Files\ArcGIS\DeveloperKit\tools\. Again,

no double quotes.6. Leave Parameters blank.7. Click OK.8. Move the new menu item up or down in the list of Tools, if you so desire.9. Click Close.10. In Delphi, click the Tools menu and click ESRI Library Locator to make sure it

works.11. Type IEnvelope in the text box and click Search or hit Enter.12. Read-only text below the text box will tell you that IEnvelope is in

esriGeometry.13. Pressing the Copy button will copy the string "esriGeometry" to the Windows

Clipboard.14. 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 doesn’t 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:

1. Go to File > Object Library References.2. See if there are lots of Libraries that start with “ESRI” and end with “Library.”

Make sure they’re checked on. You can use the Check All button to speed up the process.

3. If the ESRI libraries are missing, follow these instructions:a. Click the Add… button.b. Click the “Select by file name” radio button.c. Click the Browse… button.d. Navigate to C:\Program Files\ArcGIS\com and select all the files that

end with .olb.e. Click Open.f. Click OK. The libraries are automatically checked on.

4. 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 won’t 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. IEnvelope’s 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

Page 3: Arc Objects 9.3 in Delphi 7

“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 won’t 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.

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. It’s 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 > Environment Options, in the Type Library tab, 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 B’s pattern and not Block A’s. Also checkmark “Display updates before refreshing.” This effect is demonstrated on page 10.

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

Page 4: Arc Objects 9.3 in Delphi 7

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 7, type this in a Command Prompt:

ImportArcGISCOM 7

Create an ArcObjects 9.3 PackageI like having all of ESRI’s converted type library files in one package. I suggest

you do the same. It puts things together logically.

1. Open Delphi 7 and go to File > New > Other… 2. Click the Delphi Projects folder, then the Package icon and click OK.3. 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.

4. Compile the package. If you don't get a message about components being added to the palette, right-click to Install the package.

Check Delphi’s Library Environment SettingsWith a Delphi package saved and compiled, you are just about ready to start

programming ArcObjects with Delphi. But you have to make one more check. In Delphi, go to Tools > Environment Options and click the Library Tab. Click the ellipses next to Library Path. Make sure that $(DELPHI)\Imports is in that list. If it’s not, then add it.

As you create ArcObjects components, you’ll 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 it’s in this list of Library Paths.

Page 5: Arc Objects 9.3 in Delphi 7

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 it’s a springboard to creating your own extensions to ArcGIS. As I describe the steps, I’ll 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.

1. Open Delphi 7.2. Go to Tools > Image Editor.3. In Image Editor, go to File > New > Resource File (.res).4. Right-click on Contents, New > Bitmap.5. Type 16 and 16 as the Width and Height. Leave the Colors as VGA (16

colors). Click OK.6. Rename Bitmap1 to something like VOWELPIC.7. Double-click it to open it.8. I don’t know about you, but I don’t like working in such a small area, so I

always blow it up with Ctrl-I several times (or go to View > Zoom In several times).

9. Use the Fill command to color the whole bitmap magenta, or whatever color you want to use as the see-through color. I hate magenta, and it’s pretty common to use it as a transparent color.

10. 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.

11. 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.

12. When you’re done, close the bitmap, and save the resource file to a new folder under C:\Program Files\Borland\Delphi7\Projects that will eventually contain your Delphi project.

When you create an object that implements ITool, you should also create a Cursor resource with Image Editor, making sure to set a hot spot. The only thing I don’t like about creating cursors in Image Editor is that you’re limited to four colors: black, white, transparent, and inverse. But all the cursors that ship with ArcMap only have those colors, so I guess that’s okay.

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. ESRI’s own libraries are a good example.

1. In Delphi 7, close any open projects and go to File > New > Other… and click the ActiveX tab.

Page 6: Arc Objects 9.3 in Delphi 7

2. Click the ActiveX Library and then OK. You now have a short Project1.dpr file open in the text editor and a Project1.dll open in the Project Manager.

3. Save the project to the folder you created when making the icon. However, do NOT save it as the same name as the .res file. Delphi projects autocreate their own .res file and this would overwrite the one you created. For instance, if you saved your bitmap resource as VowelCounter.res, then you had better save your Delphi project as VowelCounterCommand.dpr. A VowelCounterCommand.res will be created in your folder.

Make an ObjectYou’ll 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 doesn’t need to do. You’ll see this when you follow the steps.

1. In Delphi 7, go to File > New > Other… and click the ActiveX tab.2. Click COM Object and then OK. The COM Object Wizard shows up.3. Type a name for the class. For this tutorial, call it LayerVowelCounter. Don’t

start the class name with a T (unless the command’s name actually begins with a T, like in TangentMaker, TripodViewWindow, or TrafficAnalyzer).

4. Leave Instancing as Multiple Instance and Threading Model as Apartment. If you want to know more about these options, consult Delphi’s Help or the Developer’s Guide for an in-depth look at these options. [Note: It doesn’t matter what you do in the Options group box. Your choices will be overridden once you complete step 7.]

5. You’ll notice that the Implemented Interface is called ILayerVowelCounter. But we don’t want to implement this interface. We want it to implement ICommand. Click List and go play 9 holes of golf while the interface list is populated. You’ll know when it’s done because the bottom of the dialog box will say “Finished loading interfaces”.

6. Click the Interface header bar to sort the interfaces. Click on any of them and just start typing 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. Don’t leave Description blank and I’ll 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, you’ll 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 library’s name. ESRI does that (e.g. esriEditor.CircleTool).

9. 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. And although you can’t see it yet, esriSystemUI_TLB has been added to the list of files in your project.

10. The first thing that pops up is your own project’s Type Library. Close that for now.

11. The class that gets created will have the standard T placed in front of its name which is why you didn’t need it in step 3. You’ll also notice it’s not a TCOMObject, but a TAutoObject. You’ll also notice the ICommand interface in the class declaration. All of the properties of ICommand have been added as well as stub implementations for them.

12. If you try and compile your project, I just know you’ll get errors and/or warnings. For instance, you’ll probably see “Declaration of ‘Get_Bitmap’ differs from declaration in interface ‘ICommand’”. What? You haven’t done

Page 7: Arc Objects 9.3 in Delphi 7

any coding yet! Delphi did all this for you and it can’t even compile what it’s generated? Here’s 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, you’d find that OLE_HANDLE is declared in esriSystem_TLB. But in your project, it thinks OLE_HANDLE is declared in the ActiveX uni. To fix the error, simply add esriSystem_TLB to your unit’s interface uses clause after the ActiveX unit.

13. Compile it again and you might get a bunch of warnings that the return value of all your functions “might” be undefined. Well, that’s because we haven’t told each function what their results are supposed to be.

14. Type the following segments of code in each of the appropriate functions:

Function Line of CodeGet_Caption Result := 'Layer Vowel Counter';Get_Category Result := 'Delphi Tools';Get_Checked Result := False;Get_HelpContextID Result := 0;Get_HelpFile Result := '';Get_Message Result := 'Click this command to

count the number of vowels in all the layers of this map.';

Get_Name Result := 'DelphiTools_LayerVowelCounter';

Get_Tooltip Result := 'Counts vowels in layer names';

15. For the Get_Bitmap function, you’ll 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;

16. To make sure this handle gets freed when your object is freed, override the class’ destructor. Call DeleteObject( hVowelIcon ); in that procedure.

17. For these two functions to work, you also have to add Windows to your uses clause (surprised that it isn’t automatic?) and also add {$R ‘VowelResource.res’} to your project’s source code (Project > View Source). You’ll get compilation errors if you include the same resource in multiple units in your project.

There are three more functions to implement, but they take some more preparation: Get_Enabled, OnClick, and OnCreate. But I better explain what it is 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 it’s stupid, but remember that the purpose of this is to help you use ArcObjects in Delphi.

18. The OnCreate function gives us a Hook into the application. To use it in the other functions, we’ll 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.

Page 8: Arc Objects 9.3 in Delphi 7

19. While you’re 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.

20. 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 doesn’t 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 QI’s. Check out the section below entitled “Query Interfacing.”

21. The Get_Enabled function uses the pMxApp variable to determine if the command should turn on. 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 //Since pMxApp is of IMxApplication, we can guarantee //that Document is of type IMxDocument. (pMxApp as IApplication).Document.QueryInterface( IMxDocument, pMxDoc ); Result := pMxDoc.FocusMap.LayerCount > 0; end;end;

The code will not compile because Delphi doesn’t know what IApplication is. If you use the ESRI Library Locator, you’ll find that IApplication is defined in esriFramework, so add esriFramework_TLB to your uses clause. Personally, I would add it to the implementation section’s uses clause.

22. 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

Page 9: Arc Objects 9.3 in Delphi 7

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 doesn’t know where to find some of the common Delphi functions. COM Libraries and object units aren’t 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 won’t 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, you’ll get a warning that it couldn’t 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 you’re adding a DLL compiled in C#, VB, or .NET technologies.

1. Open ArcMap and go to Tools > Customize.2. Click on the Commands tab.3. 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.

4. Click Add from file…5. Browse to your Delphi project folder where your .dll compiled to. You’ll also

see your type library there (.tlb), but select the DLL and click Open.6. 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.

7. Click OK.8. 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… ArcMap’s toolbars, right-click your command, and select Image & Text from the context menu, you’ll see what you wrote for the Get_Caption function. You’ll also see that text if you add your command to any menu, as menus will always show the Caption.

DebuggingDelphi 7 Professional is unable to help you debug an ArcObjects DLL from

within ArcGIS. I debug it by analyzing my code and see what could have gone

Page 10: Arc Objects 9.3 in Delphi 7

wrong--see if there’s any assumptions I’m making. I use ShowMessage’s or Log Files to track how far my code gets before crashing.

Delphi Help reports that in Tools > Debugger Options, there is an option to “Enable COM cross-process support.” But it also states that “This option may not appear in all editions of the product.” I have Delphi Professional, so maybe you can debug ArcObjects projects in Enterprise or Architect. Delphi 2005 and later can debug COM clients with the Professional-level edition.

Set Up Your Map Document For Further TestingWhen you really get into creating ArcObjects DLLs, they’ll get much more

complicated than this. Therefore, I suggest that before you program future objects for ArcMap, set up ArcMap’s 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 don’t set up a map this way, you’ll find yourself doing many repetitive tasks just to get your map in a state for testing your DLL.

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 didn’t save it and I wanted to test my DLL, thinking it would be really quick or just a small test.

Using Delphi’s 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, you’ll 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 > Environment Options and click the Type Library tab, you’ll see a checkbox labeled “Display updates before refreshing.” When that box is checked, changes you make in the Type Library window will not happen until you’ve 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

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 in Delphi 7, you don’t get the choice to specify multiple interfaces to implement. This is where the interactive Type Library window comes into play.

1. In your Delphi project, view the Type Library editor as described above.2. Click on the LayerVowelCounter CoClass.

Page 11: Arc Objects 9.3 in Delphi 7

3. Click on the Implements tab.4. Right-click the right-hand window area and choose Insert Interface.5. From the list of interfaces, choose ITool and click OK.6. For automatic code to be put in your Delphi project, click the Refresh

Implementation button (a piece of paper with the recycling symbol on it).7. A list will show up asking you to verify all of the changes that are going to be

made to your code. This is called the “Implementation file update wizard.” 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 didn’t mean to, you can turn it on under Tools > Environment Options, Type Library tab, “Display updates before refreshing” checkbox.

8. For purposes of this tutorial, click Cancel.9. Right-click the ITool interface and select Remove Interface. This exercise

was just to show you how to make objects implement multiple interfaces.

You might also have noticed that the list of interfaces was quite short. That’s 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 ESRI’s help.

2. In the Type Library editor, you’d 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.

GUID’sEverything 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. GUID’s 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 don’t have GUID’s, such as functions, properties, and enumerations.

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.

Page 12: Arc Objects 9.3 in Delphi 7

VBA to Delphi ConversionConversion Table

This 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 that 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 7 and you’ll see a whole smattering of comments dedicated to name changes. They are labeled as Hints. Some of them aren’t 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) Code Delphi CodeDim m_pEnveLope As IEnvelopeDim pFeature As IFeatureSet m_pEnvelope = New EnvelopeDim m_pPoint = New Point

var m_pEnvelope: IEnvelope; pFeature: IFeature; m_pPoint: IPoint;begin m_pEnvelope := CoEnvelope.Create as IEnvelope; m_pPoint := CoPoint.Create as IPoint;

Set pFeature = pEnumFeat.Next pFeature := pEnumFeat.Next;MsgBox pApp.Caption ShowMessage( pApp.Caption );pApp.Visible = not pApp.Visible pApp.Visible := not pApp.Visible;Set pPoint = pEnumGeometry.Next var

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

While Not pFeature Is Nothing while pFeature <> nil doDim pApp as IApplicationDim pEditor as IEditorDim pID as New UIDpID = "esriEditor.Editor"

‘Application is a global variableSet pApp = ApplicationSet pEditor =

var //pApp would be a private variable of your class. pExt: IExtension; pEditor: IEditor; pID: IUID;begin pID := CoUID.Create;

Page 13: Arc Objects 9.3 in Delphi 7

pApp.FindExtensionByCLSID(pID) 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 I’ve 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 didn’t state was that it returns an HRESULT. You can find a list of HRESULTs in Delphi under Help > Windows SDK > Index tab > HRESULTs. Essentially, bad HRESULTs start with E_ (Error) and the good HRESULT starts with S_ (success).

Sometimes you’d like to test if the object you’ve 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 will also want to 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 QI’d 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 you’re working with objects, interfaces, and variants that are assigned at run-time by ArcMap, you should try to be prepared for anything. Write code that’s 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.

You’ll 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.

Page 14: Arc Objects 9.3 in Delphi 7

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, you’ll need to know how ArcGIS makes calls to your DLL. To start, look at the bottom of your object’s unit and you’ll see a line similar to the following:

initialization TAutoObjectFactory.Create(ComServer, TLayerVowelCounter,

Class_LayerVowelCounter, ciMultiInstance, tmApartment);

You’ll notice these parameters: ComServer (the global ComServer object), TLayerVowelCounter (your custom object’s class), and the Class_LayerVowelCounter constant (a GUID found in your Type Library and declared in your type library’s .pas file). The next two parameters were options when you created your object. Look at Delphi Help under TAutoObjectFactory.Create for more of an explanation for those parameters. But here’s how the above two default values affect 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 I’ve 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 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 object’s functions. You are guaranteed that when any of your object’s 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

Page 15: Arc Objects 9.3 in Delphi 7

CoCreated two of your objects that reside in the same DLL, then two of THOSE functions COULD run at the same time, but they’ll be using their respective instance’s variables. I know—it’s 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 Delphi’s 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

unit’s 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 can’t create a form if your 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 I’ve never done this, I do have a few ideas.

1. In Delphi 7, go to File > New Application2. Go to File > New > Other... and specify Automation Object.3. Specify a CoClass Name.4. Choose Instancing to be Single Instance.5. Choose the Threading Model to be Apartment. Let’s keep it simple so we

don’t have to worry about thread support. This component is light-weight anyway, so we won’t take up a lot of time executing.

6. We can leave Generate Event support code off. After all, we’ll wait for the user to press a button before tallying the layers—we don’t need to listen to when the layers are actually added.

7. In the resulting Type Library, add ESRI SystemUI Object Library to the Uses tab.

8. Remove the automatically-generated interface.9. Add ICommand to the objects Implements tab.10. Refresh the implementation.11. Set the properties for ICommand, such as Bitmap, Enabled, etc. Add

appropriate esri*_TLB’s to your uses clauses whenever the compiler complains or when Code Insight can’t help you.

12. Add a private variable that is a TInterfaceList of IMxApplications.13. Override the Initialize procedure to create the list.14. Override the Destroy destructor to free the list.15. 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.

16. 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 aren’t valid anymore. Use try...except handlers. Your object should still recover and count the layers in the maps that still do exist.

Page 16: Arc Objects 9.3 in Delphi 7

Remember, these are just ideas. I don’t 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. Use the global Application variable as the Create parameter. To accomplish this within an ArcObjects DLL, you’ll 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 you’re programming an executable in Delphi, you should never assign the Handle property. But 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 don’t create the form in Intialize and don’t destroy it in Destroy. However, if it’s been assigned, then do create it in Initialize and destroy it in Destroy.

Page 17: Arc Objects 9.3 in Delphi 7

According to ESRI’s Help, it seems best to create forms in the OnCreate function. Tear them down in Destroy, but only after testing if they’ve 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 you’re looking for with FindField. If the result is -1, do not call the Value property to access that field’s value else ArcGIS will throw an exception. Anyway, field values are always set and returned as OleVariants.

The most important lesson I’ve learned is that if you need to concatenate an OleVariant string to a Delphi string, you just can’t 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); 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 couldn’t 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 it’s 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

Page 18: Arc Objects 9.3 in Delphi 7

a value. Note how odd it is that Null is considered a value when dealing with variants. We learned long ago that Null isn’t the same as 0 or the empty string because those are at least something. But with Variants, even Null is something. Weird. Anyway…

Another thing I’ve noticed with ArcObjects and OleVariants is what happens when you grab the value of an empty String field. Instead of getting ‘’, as you’d expect, you’ll 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.

They 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, you’ll see code like this:

Private WithEvents pGxSelection as GxSelection

In Delphi, it’s not as simple. First, your object declaration must specify that it’s implementing an outbound interface. If your object is already created and coded up, then use the Type Library editor, as described above, 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 that’s 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 that’s going to be sending you events. Using the IGxSelectionEvents example, you’ll need to listen to the GxSelection object. Obtain a reference to the object through IGxCatalog.Selection and you’ll get an IGxSelection pointer. Cast it to an IInterface pointer and store it to the IInterface variable you creatd.

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 it’s 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

Page 19: Arc Objects 9.3 in Delphi 7

used to think it wanted a pointer or an address. I read a lot in Delphi’s Help and severely tested Variant arrays and COM’s 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 it’s 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 that’s passed to you from ArcObjects is just as simple. Simply access the elements with brackets. You don’t 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 in ESRI’s Developer Help, or 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 it’s 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 project’s main source file. Pay attention to these tips when following Berend’s 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 function’s 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 function’s Result variable.

You can always add more category constants to the unit, such as c_EsriGxCommands ({5F08CBCA-E91F-11D1-AEE8-080009EC734B}), c_EsriGxViews

Page 20: Arc Objects 9.3 in Delphi 7

({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 in ESRI’s Developer Help. Generally the help pages will specify the categories your components should be in. If that’s not good enough, you can find all ESRI Category UID’s 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 you’re in an infinite loop or you’ve 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 you’re trying to debug a problem on someone else’s 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 haven’t 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 don’t see anything wrong with an ICommand or ITool logging all operations by the user.

Useful Delphi 7 Help pagesThis list is an unexhaustive list of useful help pages within Delphi 7’s Help

System. Reading these will give you more insight into programming with ArcObjects, because ArcObjects is built on the COM infrastructure.

Code generated when you import type library information How Delphi adds properties

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!