Hi,
I've created a tool that updates selected features (ie. a Watermain Valve) with the diameter of the pipe that it is attached to, and updates other attributes too. Depending on the type of selected feature, things will vary. Anyways, it seems like with one particular feature I sometimes get this error:
_COMPlusExceptionCode = -532459699
At this line of code:
//Update the feature and move to next feature
if (updated == true)
{
fCursor.UpdateFeature(feature); **this is where the error occurs!!!!
updatedFeatures++;
}
feature = fCursor.NextFeature();
The weird thing is that it doesn't happen all the time. For example, the first time I may get the error, but if I close the error window and then run the tool again (with the same feature selected) then it works.
Any ideas??
Thanks,
Hi there!
Can you post the entire method block where this error occurs? I'd have a few suggestion off the top of my head but seeing just a little more about how you open the workspace & feature class and how you are handling the feature cursor would help narrow things down.
Hi Greg,
Here is some code. Basically, when a 'fitting' is selected and this button is hit, it runs through some logic to determine what kind of fitting it is, and then adjusts a bunch of attributes to match the fitting type. This is just a small clip of code, as the entire solution does similar work for valves, service connections, etc. And it all depends on what is selected on screen when the user hits the button.
I've marked where the ERROR occurs, but it usually only happens once. Once I hit the button again, without changing the selection, it works fine the 2nd time.
Good luck....let me know if you see anything I can enhance.
#region Check for Water Fittings
if (waterFittingsLayer != null)
{
//Check for selected features in the Water Fittings
fSel = (IFeatureSelection)waterFittingsLayer;
selSet = (ISelectionSet2)fSel.SelectionSet;
if (selSet.Count > 0) //if there are any selected, then process them
{
IFeatureCursor selectedWaterService, selectedWaterMain;
IFeature waterService, waterMain;
//create a cursor for the fittings
ICursor cursor;
selSet.Update(null, false, out cursor);
IFeatureCursor fCursor = (IFeatureCursor)cursor; //create an updatedable FeatureCursor of the
//selected features
feature = fCursor.NextFeature(); //go to the first feature in the cursor
while (feature != null) //loop through all the features in the cursor
{
//A fitting can be on a WaterMain or a WaterService.
//Update the attributes of the Valve from the WaterMain first.
//Get the correct WaterMain based on the location of the Valve (feature).
//More than one WaterMain could be returned!!!
selectedWaterMain = SelectByLocation(waterMainLayer, (IPoint)feature.Shape);
waterMain = selectedWaterMain.NextFeature();
int waterMainCounter = 0;
bool waterMainExists = false;
bool updated = false;
//remove any existing Diameters from the feature
for (int i = 1; i < 5; i++)
feature.set_Value(feature.Fields.FindField("Diameter" + i.ToString()), 1);
//This will update Fittings on WaterMains
while (waterMain != null)
{
IPoint thePoint = (IPoint)feature.Shape;
IPolyline5 theLine = (IPolyline5)waterMain.Shape;
switch (waterMainCounter)
{
case 0:
feature.set_Value(feature.Fields.FindField("Diameter1"), waterMain.get_Value(waterMain.Fields.FindField("Diameter")));
//if it's not a service connection, res_service connection or generic node
//then make it a plug
if (((int)feature.get_Value(feature.Fields.FindField("SubTypeCD")) != 9) && ((int)feature.get_Value(feature.Fields.FindField("SubTypeCD")) != 6) && ((int)feature.get_Value(feature.Fields.FindField("SubTypeCD")) != 10))
{
//determine if at the end or the edge of the line
if ((thePoint.X == theLine.FromPoint.X && thePoint.Y == theLine.FromPoint.Y) || (thePoint.X == theLine.ToPoint.X && thePoint.Y == theLine.ToPoint.Y))
{
feature.set_Value(feature.Fields.FindField("SubTypeCD"), 4);
//also rotate the plug so it looks nice
if (theLine.FromPoint.X == thePoint.X && theLine.FromPoint.Y == thePoint.Y)
feature.set_Value(feature.Fields.FindField("Rotation"), CalculateAngle(theLine.ToPoint, theLine.FromPoint) * -1);
else
feature.set_Value(feature.Fields.FindField("Rotation"), CalculateAngle(theLine.FromPoint, theLine.ToPoint) * -1);
}
else //if located on the edge, then also set diameter2 for the generic node
{
feature.set_Value(feature.Fields.FindField("SubTypeCD"), 9);
feature.set_Value(feature.Fields.FindField("Diameter2"), waterMain.get_Value(waterMain.Fields.FindField("Diameter")));
}
}
waterMainExists = true;
updated = true;
//update the feature so it can calculate the case 1 properly in the next step
fCursor.UpdateFeature(feature);
break;
case 1:
feature.set_Value(feature.Fields.FindField("Diameter2"), waterMain.get_Value(waterMain.Fields.FindField("Diameter")));
//if Diam1 = current WM Diam, and it's not a service connection or generic node
//then make it a sleeve
if ((feature.get_Value(feature.Fields.FindField("Diameter1")).ToString() == waterMain.get_Value(waterMain.Fields.FindField("Diameter")).ToString()) && ((int)feature.get_Value(feature.Fields.FindField("SubTypeCD")) != 6) && ((int)feature.get_Value(feature.Fields.FindField("SubTypeCD")) != 9))
feature.set_Value(feature.Fields.FindField("SubTypeCD"), 7);
//if Diam1 is not = to WM Diam, and it's not a service connection or generic node
//then make it a reducer
if ((feature.get_Value(feature.Fields.FindField("Diameter1")).ToString() != waterMain.get_Value(waterMain.Fields.FindField("Diameter")).ToString()) && ((int)feature.get_Value(feature.Fields.FindField("SubTypeCD")) != 6) && ((int)feature.get_Value(feature.Fields.FindField("SubTypeCD")) != 9))
feature.set_Value(feature.Fields.FindField("SubTypeCD"), 5);
break;
case 2:
//if it gets this far, then it has to be a Tee
feature.set_Value(feature.Fields.FindField("Diameter3"), waterMain.get_Value(waterMain.Fields.FindField("Diameter")));
feature.set_Value(feature.Fields.FindField("SubTypeCD"), 8);
break;
case 3:
//if it gets this far, then it has to be a Cross
feature.set_Value(feature.Fields.FindField("Diameter4"), waterMain.get_Value(waterMain.Fields.FindField("Diameter")));
feature.set_Value(feature.Fields.FindField("SubTypeCD"), 3);
break;
}
if (contractNumber == true)
feature.set_Value(feature.Fields.FindField("CONTRACTNO"), waterMain.get_Value(waterMain.Fields.FindField("CONTRACTNO")));
if (installDate == true)
feature.set_Value(feature.Fields.FindField("INSTALLDATE"), waterMain.get_Value(waterMain.Fields.FindField("INSTALLDATE")));
if (dataSource == true)
feature.set_Value(feature.Fields.FindField("DATASOURCE"), waterMain.get_Value(waterMain.Fields.FindField("DATASOURCE")));
if (lifecycleStatus == true)
feature.set_Value(feature.Fields.FindField("LifecycleStatus"), waterMain.get_Value(waterMain.Fields.FindField("LifecycleStatus")));
waterMainCounter++;
waterMain = selectedWaterMain.NextFeature();
}
selectedWaterService = SelectByLocation(waterServiceLayer, (IPoint)feature.Shape);
waterService = selectedWaterService.NextFeature();
//This will update Fittings on WaterService
while (waterService != null)
{
IPoint thePoint = (IPoint)feature.Shape;
IPolyline5 theLine = (IPolyline5)waterService.Shape;
if (waterMainExists == false) //if no Water Main was intersected, then it must be a plug or service connection
{
feature.set_Value(feature.Fields.FindField("Diameter1"), waterService.get_Value(waterService.Fields.FindField("Diameter")));
if ((thePoint.X == theLine.FromPoint.X && thePoint.Y == theLine.FromPoint.Y) || (thePoint.X == theLine.ToPoint.X && thePoint.Y == theLine.ToPoint.Y))
{
feature.set_Value(feature.Fields.FindField("SubTypeCD"), 4);
if (theLine.FromPoint.X == thePoint.X && theLine.FromPoint.Y == thePoint.Y)
feature.set_Value(feature.Fields.FindField("Rotation"), CalculateAngle(theLine.ToPoint, theLine.FromPoint) * -1);
else
feature.set_Value(feature.Fields.FindField("Rotation"), CalculateAngle(theLine.FromPoint, theLine.ToPoint) * -1);
}
else
feature.set_Value(feature.Fields.FindField("SubTypeCD"), 6);
}
else //if there is a WaterMain, then it must be a Service Connection
{
feature.set_Value(feature.Fields.FindField("Diameter2"), feature.get_Value(feature.Fields.FindField("Diameter1")));
feature.set_Value(feature.Fields.FindField("Diameter3"), waterService.get_Value(waterService.Fields.FindField("Diameter")));
//if the water service is a res_service then create a res_serviceConnection
//else make a regular service connection
if ((int)waterService.get_Value(waterService.Fields.FindField("SubTypeCD")) == 3)
feature.set_Value(feature.Fields.FindField("SubTypeCD"), 10);
else
feature.set_Value(feature.Fields.FindField("SubTypeCD"), 6);
}
if (contractNumber == true)
feature.set_Value(feature.Fields.FindField("CONTRACTNO"), waterService.get_Value(waterService.Fields.FindField("CONTRACTNO")));
if (installDate == true)
feature.set_Value(feature.Fields.FindField("INSTALLDATE"), waterService.get_Value(waterService.Fields.FindField("INSTALLDATE")));
if (dataSource == true)
feature.set_Value(feature.Fields.FindField("DATASOURCE"), waterService.get_Value(waterService.Fields.FindField("DATASOURCE")));
if (dataSource == true)
feature.set_Value(feature.Fields.FindField("LifecycleStatus"), waterService.get_Value(waterService.Fields.FindField("LifecycleStatus")));
waterService = selectedWaterService.NextFeature();
updated = true;
}
//Update the feature and move to next feature
if (updated == true)
{
fCursor.UpdateFeature(feature); *****THIS IS WHERE THE ERROR OCCURS
updatedFeatures++;
}
feature = fCursor.NextFeature();
//set these to null before moving to the next Fitting
selectedWaterService = null;
waterService = null;
selectedWaterMain = null;
waterMain = null;
}
}
}
#endregion
Try using the ComReleaser class to manage the lifetime of the update FeatureCursor. The ComReleaser will release the cursor objects when it goes out of scope and decrement the number of Runtime Callable Wrappers (RCWs). You can read more on COM Interop and RCWS in the documentation; I'm sure that my use of terms is not quite right.
What happens is that as you open up new cursors, the number of RCWs starts to add up and that's when things go awry. So it makes sense that it would run a few times and then fail and that when you restart it works fine again.
//create a cursor for the fittings
ICursor cursor;
selSet.Update(null, false, out cursor);
using (ComReleaser comReleaser = new ComReleaser())
{
IFeatureCursor fCursor = (IFeatureCursor)cursor;
comReleaser.ManageLifetime(fCursor);
// ... everything until you are done with the cursor;
}
Hi Greg,
Thanks for the idea. I've adjusted my code as you stated, but it's not making any difference. I still get the same error at the same line of code.
I run this same bit of code for other features too; hydrants, valves, etc, but I never get this same error in those blocks of code. Only when doing the fittings features. Even if I only select a single fitting, the same error happens the first time 'round.
I'm certain the error is happening with that Fittings code block, but I have no idea what is causing it.
Any other ideas??
I couldn't find a match for the error code you had posted, what other information is there about the error? If we can match it to an ESRI error code we might be able to narrow it down further.
Are you managing / releasing all the other cursors here as well (e.g. selectedWaterMain, selectedWaterService)?
You say that this is with one particular feature... is there anything strange about the geometry? If you make a new feature class with only that feature, do you still get the error?
Hi Greg,
Other than doing this (see below) at the end of each code block, I am not doing anything else to 'manage' the cursors:
//set these to null before moving to the next Fitting
selectedWaterService = null;
waterService = null;
selectedWaterMain = null;
waterMain = null;
Our production environment is SDE (SQL Server), and I'm use a File GDB for testing. The same problem appears in either.
Here is the exact error:
No match for that error code - I still think that this is an RCW thing but hard to tell without seeing it run through in debug mode.
Note that setting cursor references to null doesn't always release the references right away. Either use the ComReleaser class by adding each cursor to the comReleaser.ManageLifetime(any_cursor):
//More than one WaterMain could be returned!!!
selectedWaterMain = SelectByLocation(waterMainLayer, (IPoint)feature.Shape);
comReleaser.ManageLifetime(selectedWaterMain);
and
selectedWaterService = SelectByLocation(waterServiceLayer, (IPoint)feature.Shape);
comReleaser.ManageLifetime(selectedWaterService);
or explicitly call Marshal.ReleaseComObject(cursor) where you are setting them to null, e.g.:
int wsCount = Marshal.ReleaseComObject(selectedWaterService);
System.Diagnostics.Debug.WriteLine(wsCount.ToString() + " water service references remaining."); // Just to see how many are remaining.
One more thing I noticed is that in case 0, UpdateFeature is called as well. I'm not sure if that would have anything to do with it...
This looks so much like a case of a reference to a feature cursor not being released before you open another one up. If explicitly releasing them doesn't work, I would start looking at how you are opening the workspaces ..( ArcObjects 10 .NET SDK Help )
I will step through the code a bit more and see if something else is happening that I'm not quite catching.
I'll keep you posted.