Sei sulla pagina 1di 23

Programming with

ESRIs 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 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 Development


Since 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 Development


Delphi needs some prepping before you start using ArcObjects. Follow these
instructions as best you can.

Get the Latest Delphi 2009 Updates


Delphi 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 Menu


The 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.
1.
2.
3.
4.

In Delphi, go to Tools > Configure Tools


Click Add
For the Title, type ESRI Library Locator
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 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:
1.
2.
3.

4.

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


IDE
When 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 </rss> tag, insert the following text:
<channel>
<title>ESRI News Feeds</title>
<item>
<title>ESRI Main Feed</title>
<link>http://www.esri.com/news/rss/rss.xml</link>
</item>
<item>
<title>ESRI Support - Common Issues</title>
<link>http://rss.esri.com/support/commonIssues.xml</link>
</item>
</channel>
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 <item> tag to the above <channel>
tag. The <item> tag will have to have <title> and <link> 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 Settings


In the code blocks below, assume pPoint is of type IPoint. Which code would
you rather write?
CODE BLOCK A
var
dXValue: Double;
begin
pPoint.Get_X(dXValue);
if dXValue = 0 then
begin
...
end;
end;
CODE BLOCK B
begin
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 11.

Import ArcGIS Libraries


Before 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 Package


I like having all of ESRIs converted type library files in one package. I suggest
you do the same. It puts things together logically.
1.
2.

3.
4.

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 Settings


With 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 Project


This 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 Cursors


Before I even begin a project, I create an icon for the tool.
1.
2.
3.

4.
5.
6.

7.

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 Library


Any 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.
1.

2.

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 2009


Putting 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 > Resources
2. 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 Object
Youll 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 1. 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:
Function
Get_Caption
Get_Category
Get_Checked
Get_HelpContextID
Get_HelpFile
Get_Message
Get_Name
Get_Tooltip

Line of Code
Result := 'Layer Vowel Counter';
Result := 'Delphi Tools';
Result := False;
Result := 0;
Result := '';
Result := 'Click this command to
count the number of vowels in all
the layers of this map.';
Result :=
'DelphiTools_LayerVowelCounter';
Result := '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 7.
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 ArcMap


The 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.
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. Youll 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 Command


If 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.
Debugging
Debugging 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 angle
so it keeps asking every command if its enabled.
Set Up Your Map Document For Further Testing
When 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 Editor


With 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 walkthrough below.
Implementing Multiple Interfaces
As 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.
1.

In your Delphi project, view the Type Library.

2.
3.
4.
5.
6.

7.

8.
9.

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 tab
4. 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.
GUIDs
Everything 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 Conversion


Conversion 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 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) Code
Dim m_pEnveLope As IEnvelope
Dim pFeature As IFeature
Set m_pEnvelope = New Envelope
Dim m_pPoint = New Point

Set pFeature = pEnumFeat.Next


MsgBox pApp.Caption
pApp.Visible = not pApp.Visible
Set pPoint = pEnumGeometry.Next

While Not pFeature Is Nothing


Dim pApp as IApplication
Dim pEditor as IEditor
Dim pID as New UID
pID = "esriEditor.Editor"
Application is a global variable
Set pApp = Application

Delphi Code
var
m_pEnvelope: IEnvelope;
pFeature: IFeature;
m_pPoint: IPoint;
begin
m_pEnvelope := CoEnvelope.Create
as IEnvelope;
m_pPoint := CoPoint.Create as
IPoint;
pFeature := pEnumFeat.Next;
ShowMessage( pApp.Caption );
pApp.Visible := not pApp.Visible;
var
pGeometry: IGeometry;
pPoint: IPoint;
...
begin
...
//IPoint inherits from IGeometry
pGeometry := pEnumGeometry.Next;
Supports( pGeometry, IPoint,
pPoint );
while pFeature <> nil do
Var
//pApp would be a private
variable of your class.
pExt: IExtension;
pEditor: IEditor;
pID: IUID;
begin

Set pEditor =
pApp.FindExtensionByCLSID(pID)

pID := CoUID.Create;
pID.Value :=
GUIDToString( esriEditor.CLASS_Edi
tor );
//pApp assigned during OnCreate
or something
pExt :=
pApp.FindExtensionByCLSID( pID );
Supports( pExt, IEditor, pEditor
);

Other ArcObjects-Related Delphi Programming Tips


Years 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).
Supports
Sometimes 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 Handling
Try...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 Destructor
Whether 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 Threading
First 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 Variables
One 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.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.

15.

In Delphi 2009, double-click Automation Object in the "Delphi Projects |


ActiveX" category of the component palette.
Specify a CoClass Name.
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.
Choose Instancing to be Single Instance.
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.
In the resulting Type Library, add ESRI SystemUI Object Library to the Uses
tab.
Remove the automatically-generated interface.
Add ICommand to the objects Implements tab.
Refresh the implementation.
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.
Add a private variable that is a TInterfaceList of IMxApplications.
Override the Initialize procedure to create the list.
Override the Destroy destructor to free the list.
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.
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 Forms
Using 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.
OleVariants
OleVariants 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. Anyway
Another 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 Functions
Some 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 Categories
When 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 Logging
ShowMessages 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 pages
Reading 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 Document


Future 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!

Potrebbero piacerti anche