COM object that has been separated from its underlying RCW cannot be used.

5751
11
Jump to solution
09-06-2018 02:23 AM
Harald_ØysteinLund1
Esri Contributor

Hi, 

I have created a EditorExtention in a ArcMap C# Add-in. Here I hook up to editor events like OnStartEditing, OnStopEditing OnCteateFeature etc.

I also wan't to listen to the OnBeginreconcile event to prevent the user to post incomplete data. This I have managed to obtain and it works well using editing tools, reconcile and undo the reconcile if the data isn't valid.

For one edit task, we have set up a editing window to create a complex feature, a lighthouse with sectors. To run this task, Editing has to be started by the user with standard ArcMap tools, and a lighthouse has to be created, also with standard tools. The lighthouse has to be selected, and the user clicks; a button to open the custom Sector Editing window. In the StartEditing event we hookup to the OnReconcile and OnBeginReconcile events with the following Code:

private IVersionEvents2_Event VersionEvents2
 {
 get {
 return ArcMap.Editor.EditWorkspace as IVersionEvents2_Event; 
 }
 }

private IVersionEvents_Event VersionEvents
 {
 get
 {
 return ArcMap.Editor.EditWorkspace as IVersionEvents_Event;
 }
 }


void Events_OnStartEditing()
 {
 

//Wire OnCreateFeature edit event.
 Events.OnCreateFeature += new IEditEvents_OnCreateFeatureEventHandler
 (Events_OnCreateFeature);
 //Wire onChangeFeature edit event.
 Events.OnChangeFeature += new IEditEvents_OnChangeFeatureEventHandler
 (Events_OnChangeFeature);
 //Wire onChangeFeature edit event.
 Events.OnDeleteFeature += new IEditEvents_OnDeleteFeatureEventHandler 
 (Events_OnDeleteFeature);
 Events2.OnSaveEdits += new IEditEvents2_OnSaveEditsEventHandler(Events_OnSaveEdits);
 Events2.OnStartOperation += new IEditEvents2_OnStartOperationEventHandler(Events_OnStartOperation);
 //VersionEvents2.OnPost += new IVersionEvents2_OnPostEventHandler(Events_OnPost); 
 Events2.OnStopOperation += new IEditEvents2_OnStopOperationEventHandler(Events_OnStopEditOperation);
 VersionEvents2.OnBeginReconcile += new IVersionEvents2_OnBeginReconcileEventHandler(Events_OnBeginReconcile);
VersionEvents.OnReconcile += new IVersionEvents_OnReconcileEventHandler(Events_OnReconcile);
 WiredUp = true;
 
 }

When the user describe each sector in this customized window with database grid window and click the window's save button, the following process is run:

StartEditingOperation

   Create the new sectors and related them to the selected lighthouse

   Update any existing sectors.

StopEditingOperation

StartEditingOperation

   Delete those sectors that is deleted.

StopEditingOperation

StartEditingOperation

   Create glass information for each sectors if the user has filled out this information

   Relate the glass info to each sector.

StopEditingOperation

User close the window.

I the user clicks reconcile, the event OnBeginReconcile should fire, and that happens most of the times when I run the testes. But some times the PostButton is enabled and no the event code we have created does not execute.

When the user clicks StopEditing and we then have to unwire the events, the following code fails:

VersionEvents2.OnBeginReconcile -= new IVersionEvents2_OnBeginReconcileEventHandler(Events_OnBeginReconcile);
VersionEvents.OnReconcile -= new IVersionEvents_OnReconcileEventHandler(Events_OnReconcile);

The error that is casted:

COM object that has been separated from its underlying RCW cannot be used.

So doing editing by the book calling start and stop editioperation and only calling System.Runtime.InteropServices.Marshal.ReleaseComObject(obj) for ICursor objects, we get this behavior some times, and some times not.

I have even got VersionEvents2.OnBeginReconcile -= new ... line to run ok, but the next line fails. This is very odd since the pointers point to the same object!?

So the reason that the event isn't always caught by our code, is the the COM object has been separated from its underlying RCW.

Now the question: Do ESRI run any marshal releasecomobject that on the EditWorkspace object and the GC destroys the object. 

I've done some changes in the process/code to see if I could simplify the processes and I have also read through the code to see if I do some releasecomobject where I shouldn't.

  1. Made one StartEditOperation ans StopEditOperation for the whole save process for the Sector editing.
  2. Uncommented the function that run the System.Runtime.InteropServices.Marshal.ReleaseComObject(obj) to make sure it isn't called at all by my code.
  3. Also tried to do the editing without Start and Stop editoperation, but that gave another error not related to this at all.
  4. On the windows closing I have tried to unwire and wire up the events, but here it fails with the same error message, COM separation.

So nothing helps. Is this a bug? I haven't stripped this down to a small add-in to see if I could reproduce the error in a smaller scale. That maybe my next approach. It seems that the ArcMap.Editor.Editworkspace is release.

It doesn't get fixed even when we try to stop and start  the editing again. Looks like ArcMap.Editor.EditWorkspace is lost.

Maybe I could try to reconnect to he application, but I don't believe that would help, since we're just pointing to a active arcmap application.

Any one that has experienced this?

 

0 Kudos
1 Solution

Accepted Solutions
JeffMatson
Occasional Contributor III

OnStartEditing and OnStopEditing are the correct locations to wire/unwire your version events, but I don't see anywhere in your code where IMapEvents_VersionChanged is set up.  That would need to be wired/unwired in a different location and may explain the issue.  I have a method in my project that resets the workspace inside the version changed event.

If that doesn't help perhaps post all your (relevant) code and something will jump out...

View solution in original post

11 Replies
GKmieliauskas
Esri Regular Contributor

Hi Harald,

You could get that type of error in code like this:

public IRow ReturnRow()

{

   IRow pRow = null;

   try {

      // Some code to create or get pRow

      return pRow;

   }

   catch() {

   }

   finally {

      Marshal.ReleaseComObject(pRow);

   }

}

Marshal.ReleaseComObject in finally section is the reason of "COM object has been separated from its underlying RCW" error. You will get exception at first using returned IRow object:

IRow pRow = ReturnRow();

pRow.Delete(); // Here you will get exception

IRow is just example. It could be every COM object. In your case it could be IWorkspaceEdit object.

0 Kudos
Harald_ØysteinLund1
Esri Contributor

Hi  Gintautas,

Thank you for your replay.

Yes your right, that could be the reason. The problem is that I do not do any ReleaseComObject on the Workspace. It is only done on the ICursors or IFeatureCursor. I have also tested to not do any ComRelease to check if there is a problem with my code here. I still get this error. It is like that the object is released within Esri code. So if I do not call any ReleaseComObject, then the object should live. Or could the GC kick in and the object is marked as released within ESRI code? I don't know. I'm struggling to isolate this error in a reproducible sample, but hope I'm able to to that soon since this is really tricky to debug.

0 Kudos
GKmieliauskas
Esri Regular Contributor

Hi Harald,

I have looked at your code one more time and found that you are using code like this:

return ArcMap.Editor.EditWorkspace as IVersionEvents_Event;

Sequential calling of methods is the fast way to write code but is not the best on error handling and other cases.

I would recommend to read EditWorkspace and save to class variable before returning. Return value of class variable.

Then you would lock EditWorkspace object from releasing. On your class destructor, Dispose method or then you do not need it you can release EditWorkspace object

Harald_ØysteinLund1
Esri Contributor

Thank you Gintautas for your answer.

Yes I should try out that. 

I will look into this as soon as possible. 

Harald

0 Kudos
Harald_ØysteinLund1
Esri Contributor

I've now tried to keep the Workspace in a variable, but that didn't help. And I don't think keeping a variable for an object would prevent other operations to kill an instance of an object since they only are pointers. So we end up with pointers that points to memory address area that are cleared by the RCW. That's the reason of the error message. But since I do no Marshal release of the Workspace object. I still wounder where this happens in the process. I do release IFeatureCursors and ICursor referenced objects, but nothing else. I have not been able to reproduce this in a sample add-in yet, so then I believe there must be some combinations in my original code that causes this. Hard to ask for help when this is tricky to isolate.

0 Kudos
JeffMatson
Occasional Contributor III

Hi Harald,

Event variables can go out of scope at "unexpected" times.  You'll need to wire/unwire event handlers and hydrate variables at all event boundaries.

For example if someone opens a particular mxd, the OpenDocument event fires, followed by BeforeCloseDocument and CloseDocument, then another OpenDocument.  Any variables hydrated at the first OpenDocument will have gone out of scope and the RCW error will occur the next time they are referenced.

Instead you would want to use modular level variables which are hydrated at OpenDocument and released at BeforeCloseDocument, then hydrated again during the next OpenDocument so you have a "fresh" instance.

This event lifecycle is similar for Map events, Edit events, etc.  Below is an outline that should help for a simple MXD with one map in it.  It gets more complicated when switching between multiple data frames, edit sessions and workspaces, etc.

Startup event fires

   (+DocumentEvents - set up listeners for OpenDocument, NewDocument and BeforeCloseDocument)

      OpenDocument/NewDocument event fires (initial ArcMap opening event)

         (+MapEvents)

         (+EditEvents)

      BeforeCloseDocument event fires (this fires before the actual MXD that was chosen opens)

        (-EditEvents)

        (-MapEvents)

      OpenDocument/NewDocument event fires   (now we are in the MXD selected for opening and ready for action)

         (+MapEvents)

         (+EditEvents)

   ----------------------------------------------------------------

   The bulk of operations occurring in your session

   ----------------------------------------------------------------

   

   BeforeCloseDocument event fires  (session is done, user is either opening another MXD or closing ArcMap)

      (-EditEvents)

      (-MapEvents)

Shutdown event fires   (user is definitely closing ArcMap at this point)

   (-DocumentEvents)

Harald_ØysteinLund1
Esri Contributor

Hi Jeff,

Thank you for your answer.

I do the following wire and unwire in the OnStartEditing on OnStopEditng. That should be with in the scope of editing, but do you think that I should wire up the IVersionEvents_OnBeginReconcileEvent in the OnStartEditOperation? I think that at that point the OnBeginReconcileEvent has already fired, because that is an edit operation. So I can't understand where else to wire up an unwire the event other that OnStartEditing an OnStopEditing.

But if I start edit operations with code within this editing process, may that disturb the scope? If that is the case, then I can't understand that there is possible to interact with the reconcile process.


					
				
			
			
				
			
			
				
			
			
			
			
			
			
		
0 Kudos
JeffMatson
Occasional Contributor III

OnStartEditing and OnStopEditing are the correct locations to wire/unwire your version events, but I don't see anywhere in your code where IMapEvents_VersionChanged is set up.  That would need to be wired/unwired in a different location and may explain the issue.  I have a method in my project that resets the workspace inside the version changed event.

If that doesn't help perhaps post all your (relevant) code and something will jump out...

Harald_ØysteinLund1
Esri Contributor

Aha! That could off course be the reason. How do you reset the workspace, just get det workspace from Search for the EditorExt by ID and get the workspace again? I'll will try that out!

I've now tried this out.

I wire up the MapEvents.VersionChanged event on the Document OpenDocument, and in the VersionChanged delegate I get the current workspace from the new version:  

_wsEdit = (IWorkspace)newVersion;

I've tested out this a couple of times now, and I can't seem to reproduce the error. I will rebuild the Add-in and let my customer test it as well.

As you wrote Jeff, I also unwire the VersionChanged event in the CloseDocument Event, and the OpenDocument, CloseDocument is wired up in startup of the EditorExtension. 

Thanks a lot for the answer. I thought that the active edit workspace was obtained by the editorExtension Editor object, but the IVersion points to the Geodatabase edit workspace. I think that is the reason for the unstable behaivour, that the ArcMap.Editor.WorkspaceEdit was lost when version is changed. So following the principle that you pointed out so well Jeff, gives me better control.