Documenti di Didattica
Documenti di Professioni
Documenti di Cultura
4.
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.
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!
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
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.
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?
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.
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.
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.
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.
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.
2.
3.
4.
5.
6.
7.
8.
9.
[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.
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
);
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.
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.
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.
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!