ArcMap 10, ArcObjects, and Python:  very cool, but help with a couple of problems?

3988
5
10-26-2010 07:21 AM
TiCrossman
New Contributor
VITALS:  ArcGIS 10.0 - ArcView, Python 2.6.5, comtypes 0.6.2

I am writing an external Python script that needs to access the running ArcMap 10 application; therefore, I am getting my app reference through the AppROT object, which is working fine, as far as that goes.  So, here are my problems/questions:

1)  What object type is returned when I execute the line  pApp=pAppROT.Item(i)?  I definitely get an IApplication for ArcMap but on what object exactly?  I thought it was IApplication in the esriArcMapUI.olb, but that may not be the case ... esriFramework.olb maybe?  [BTW, pAppROT is the IAppROT interface on the AppROT object.]  It makes a difference, because I later QI to IMxDocument and get an error that the module (esriArcMapUI) has no attribute IMxDocument.  This may all relate to my 2nd question:

2)  I am building Python wrappers for ESRI olb's on the fly using the GetModule function in comtypes.  The problem is that wrappers are not being built for all of the olb's I specify, or rather the wrapper .py modules (the ones with the long GUID names) are empty.  esriFramework, esriGeometry, esriSystem, and esriSystemUI build just fine.  esriArcMapUI, esriCarto, and esriGeoDatabase do not build.  Has anyone else had this problem?  If so, how did you fix it ... if you did , that is?

Thank you all for the help.

-- Ti
0 Kudos
5 Replies
FrankPerks
New Contributor II
1. Your first problem is related to you second:

2. If you run the following (this will generate wrappers for every ESRI com lib):

import os
import comtypes.client
# change com_dir to whatever it is for you
com_dir = r'C:\Program Files (x86)\ArcGIS\Desktop10.0\com'
coms = [os.path.join(com_dir, x) for x in os.listdir(com_dir) if os.path.splitext(x)[1].upper() == '.OLB']
map(comtypes.client.GetModule, coms)

# check add whatever you want here.
import comtypes.gen.esriArcMapUI
import comtypes.gen.esriGeodatabase

print dir(comtypes.gen.esriArcMapUI)


Do you get anything from the dir?

If not then run this:

import logging
# grab rootlogger
_loggy = logging.getLogger()
_loggy.setLevel(logging.DEBUG)
_loggy.addHandler(logging.FileHandler("derpdebug.log"))
import os
import comtypes.client
# change com_dir to whatever it is for you
com_dir = r'C:\Program Files (x86)\ArcGIS\Desktop10.0\com'
coms = [os.path.join(com_dir, x) for x in os.listdir(com_dir) if os.path.splitext(x)[1].upper() == '.OLB']
map(comtypes.client.GetModule, coms)

# check add whatever you want here.
import comtypes.gen.esriArcMapUI
import comtypes.gen.esriGeodatabase

print dir(comtypes.gen.esriArcMapUI)


There should now be a derpdebug.log file now (where ever you ran it from), open it up and look for, anything that indicates an error, also make sure you see the GetModule call for esriArcMapUI.

Worst case, delete everything in <PYTHON_DIR>\comtypes\gen directory (python_dir is your python installation path).

If still this does not work, let me know.
0 Kudos
TiCrossman
New Contributor
Frank,

Thank you so much for your help.  Your solutions worked like a charm.  When I ran your script, the dir listed everything in the __dict__ of esriArcMapUI, as it should.  esriArcMapUI.py was intact.  With help from the person who wrote the example code that I used as a base, I have discovered that the comtypes generation of wrappers for ArcObjects occasionally fails, creating blank wrapper files.  This seems to happen when a wrapper file already exists and GetModule() is called to build it again.  I tested the wrapping process many more times, with both existing wrappers and a cleared out gen cache directory.  I got a failure once, and that was for an existing wrapper.  No failures for an empty cache.  Also, the failures all produced readable but empty files.  I have not encountered any instances of missing wrappers (where there should be one) or of corrupted, unreadable wrappers.  I did also run your second, debug script quite a few times, and dir() reported as expected.  The debug logs also showed no errors.  So, I have modified my code to check for existing (the more secure open(filepath) way) wrappers and whether those existing modules are blank or not before I make any calls to GetModule().  Works great.  I'll just be sure to rerun the wrapping process after ArcObjects updates.  Thanks, again, Frank.  I only wish I could give you some MVP points.  Grrrr.

Cheers,

Ti
GIS Specialist
Durham, NH
0 Kudos
FrankPerks
New Contributor II
Frank,

Thank you so much for your help.  Your solutions worked like a charm.  When I ran your script, the dir listed everything in the __dict__ of esriArcMapUI, as it should.  esriArcMapUI.py was intact.  With help from the person who wrote the example code that I used as a base, I have discovered that the comtypes generation of wrappers for ArcObjects occasionally fails, creating blank wrapper files.  This seems to happen when a wrapper file already exists and GetModule() is called to build it again.  I tested the wrapping process many more times, with both existing wrappers and a cleared out gen cache directory.  I got a failure once, and that was for an existing wrapper.  No failures for an empty cache.  Also, the failures all produced readable but empty files.  I have not encountered any instances of missing wrappers (where there should be one) or of corrupted, unreadable wrappers.  I did also run your second, debug script quite a few times, and dir() reported as expected.  The debug logs also showed no errors.  So, I have modified my code to check for existing (the more secure open(filepath) way) wrappers and whether those existing modules are blank or not before I make any calls to GetModule().  Works great.  I'll just be sure to rerun the wrapping process after ArcObjects updates.  Thanks, again, Frank.  I only wish I could give you some MVP points.  Grrrr.

Cheers,

Ti
GIS Specialist
Durham, NH


The way comtypes generates modules is a goldmine of horrors. GetModule only checks if the module is importable (via importerror), and does not check if the module actually finished generation. Technically it should hold off of writing the modules until all other linked modules are finished processing, sadily it does not (this bug has been in their bug tracker for quite awhile last time i checked.)

If you are deploying a script that uses comtypes, the safest way is to modify the distutils setup.py file(if your not using one then make a script that is run once, when the script is installed). So:

1. Nuke the comtypes.gen folder
2. call GetModule() on all of the comlibs in the ArcGIS/com folder.

This prevents the need for GetModule() to be used within your actual program. Which hopefully makes things a little easier.
0 Kudos
PhilMorefield
Occasional Contributor III
Frank,

I'm only just learning ArcObjects, and I'm hoping you can throw a little advice my way. Based on what I've read so far, I thought the following would work:

import arcpy
import comtypes
import comtypes.client
import comtypes.gen.esriDataSourcesNetCDF

ws_factory = comtypes.client.CreateObject("esriDataSourcesNetCDF.NetCDFWorkspaceFactory", interface = esriDataSourcesNetCDF.INetCDFWorkspaceFactory)


However, this throws an error since there is "no attribute 'INetCDFWorkspaceFactory'. As I understand it, Step 1 is to create a workspace factory. Step 2 is to create the workspace. And Step 3 is to instantiate the file of interest (in this case a NetCDF file) so I can play with the data.

Can you steer me back on course?
0 Kudos
MikeHunter
Occasional Contributor
Try this.  It should work:


import comtypes.gen.esriDataSourcesNetCDF as esriDataSourcesNetCDF
import comtypes.gen.esriGeoDatabase as esriGeoDatabase

ws_factory = comtypes.client.CreateObject("esriDataSourcesNetCDF.NetCDFWorkspaceFactory", interface = esriGeoDatabase.IWorkspaceFactory)

In AO, a class may be in 1 library, and an interface to that class may be in another.  Also, do the imports like I did above--it will help your IDE to follow what's going on and help you with autocompletes.

good luck,
Mike
0 Kudos