TheSwamp

Code Red => .NET => Topic started by: Atook on June 01, 2020, 02:50:58 PM

Title: Chasing down and understanding eInvalidContext..
Post by: Atook on June 01, 2020, 02:50:58 PM
I'm trying to chase down an eInvalidContext error. Unfortunately I can't replicate it on my development machine and I only have error reports to work with. My suspicion is that it's tied to closing a transaction based on the limited results of my web searching. (https://forums.autodesk.com/t5/net/polyline-convertto-throws-einvalidcontext-in-acad-2019/td-p/9052318)

The error appears to be caused from my code just after committing the transaction(line 39 below). Using an OpenCloseTransaction may solve the problem, but I"m not sure if it will, and I'd like to understand what's happening better. I have to admit, I'm not even understanding what the context is within this code.

Can anyone here shed light on why eInvalidError might be occurring here?

The stacktrace in my error reporting is

Code: [Select]
moduleAutodesk.AutoCAD.DatabaseServices.Transaction
context-lineVoid DeleteUnmanagedObject()

moduleAutodesk.AutoCAD.Runtime.DisposableWrapper
context-lineVoid !DisposableWrapper()

moduleAutodesk.AutoCAD.Runtime.DisposableWrapper
context-lineVoid Dispose(Boolean)

moduleAutodesk.AutoCAD.Runtime.DisposableWrapper
context-lineVoid Dispose()

moduleAID.CAD_Utility.Layers
context-lineBoolean MakeLayer(System.String, Int16, Boolean)

For context the code is called from a command call, which is where I catch the error and report it.
Code - C#: [Select]
  1. [CommandMethod("IR_AssignDripFlows")]
  2. public static void AssignDripFlowsToBlocks()
  3. {
  4.   try
  5.   {
  6.     DripFactory.AssignDripFlows();
  7.   }
  8.   catch (Exception e)
  9.   {
  10.     AID_Application.HandleError(e);
  11.   }
  12. }

The code appears to be throwing the error is:
Code - C#: [Select]
  1. /// <summary>
  2. /// Makes the layer, makes sure it's thawed and on
  3. /// </summary>
  4. /// <param name="layerName">Name of the layer.</param>
  5. /// <param name="color">The color.</param>
  6. /// <param name="print">whether or not the layer prints</param>
  7. /// <returns>True if the layername is valid, false if it's not</returns>
  8. public static bool MakeLayer(string layerName, short color=7, bool print = true)
  9. {
  10.   if (Utililty.IsValidSymbolName(layerName))
  11.   {
  12.     using (Active.Document.LockDocument())
  13.     {
  14.       using (Transaction tr = Active.Document.TransactionManager.StartTransaction())
  15.       {
  16.         LayerTable layerTable = (LayerTable)tr.GetObject(Active.Database.LayerTableId, OpenMode.ForWrite);
  17.         if (!layerTable.Has(layerName))
  18.         {
  19.           LayerTableRecord newLayer = new LayerTableRecord
  20.           {
  21.             Name = layerName,
  22.             Color = Color.FromColorIndex(ColorMethod.ByAci, color),
  23.             IsPlottable = print
  24.           };
  25.           layerTable.Add(newLayer);
  26.           tr.AddNewlyCreatedDBObject(newLayer, true);
  27.         }
  28.         else
  29.         {
  30.           // make sure it's thawed and on. (maybe we should unlock it?)
  31.           LayerTableRecord ltr = tr.GetObject(layerTable[layerName], OpenMode.ForWrite) as LayerTableRecord;
  32.           if (ltr != null)
  33.           {
  34.             if (ltr.IsFrozen) ltr.IsFrozen = false;
  35.             ltr.IsOff = false;
  36.           }
  37.         }
  38.         tr.Commit();
  39.       }//<-eInValidContext throws here
  40.     }
  41.   }
  42.   else
  43.   {
  44.     Active.WriteMessage("\nInvalid Layername: " + layerName);
  45.   }
  46.   return Utililty.IsValidSymbolName(layerName);
  47. }


Active.Document is just a helper class as follows:
Code - C#: [Select]
  1. ...
  2. using _CadAPP = Autodesk.AutoCAD.ApplicationServices.Application;
  3. ...
  4. public static class Active
  5. {
  6.   public static Document Document
  7.   {
  8.     get { return _CadAPP.DocumentManager.MdiActiveDocument; }
  9.   }
  10. }
  11. ...
Title: Re: Chasing down and understanding eInvalidContext..
Post by: huiz on June 01, 2020, 04:29:08 PM
Do you get an ObjectId from LayerTable[layerName] or a LayerTableRecord? Then you don't need the GetObject function.
Title: Re: Chasing down and understanding eInvalidContext..
Post by: huiz on June 01, 2020, 04:30:32 PM
And a tip, a deleted layer exist untill the drawing is closed. You don't check that.
Title: Re: Chasing down and understanding eInvalidContext..
Post by: huiz on June 01, 2020, 04:34:51 PM
And you could have wrapped everything in a Try-Catch so you could read the System.Exception value. That makes sense in a lot of cases.


 :yes:
Title: Re: Chasing down and understanding eInvalidContext..
Post by: Jeff H on June 01, 2020, 06:08:49 PM
Also as gile mentioned in your link(will be easier to just quote him than reword it)
Quote
What i was asking is: in which context do you call this method? from a CommandMethod? a modal dialog? a modeless palette? ...
IOW, you should show the code which call this method.
You can have VS find all references to method but make you test everywhere it is called from and might be different contexts.
Title: Re: Chasing down and understanding eInvalidContext..
Post by: Atook on June 01, 2020, 09:11:13 PM
Thanks for the replies.

The code is called from a command call, which is where I catch the error and report it. I certainly should have included that in the original post.
Code - C#: [Select]
  1. [CommandMethod("IR_AssignDripFlows")]
  2. public static void AssignDripFlowsToBlocks()
  3. {
  4.   try
  5.   {
  6.     DripFactory.AssignDripFlows();
  7.   }
  8.   catch (Exception e)
  9.   {
  10.     AID_Application.HandleError(e);
  11.   }
  12. }

The Exception in the catch is an Autodesk.AutoCad.Runtime.Exception. If I change that to a System.Exception will I get better data as far as tracking down the issue?

@huiz, thanks for pointing out the deleted layers still in the database, I'll have to add that into the function.

I've gotten the eInvalidContext error in a few other functions as well, and it seems to be after committing a transaction. This layer funcion is an example, I'm trying to understand what causes the eInvalidContext, and how to structure my code differently.
Title: Re: Chasing down and understanding eInvalidContext..
Post by: huiz on June 02, 2020, 03:30:55 AM
Uhm, weird. I can only find little information about eInvalidContext where it refers to a Transaction resident objects or another database. I can't reproduce the error.


Further I notice that the Has() function does not return deleted Layers, while I am pretty sure it did in the past. So you don't have to check on deleted Layers anymore I assume.  :thinking:


I never use the Autodesk Runtime Exception but always System.Exception. The latter is the mother of all exceptions. I know you can catch several exception types and do things according to that exception but being lazy I prefer one catch all. I don't know if you get a better description in this case, you can try and see what happens.
Title: Re: Chasing down and understanding eInvalidContext..
Post by: n.yuan on June 02, 2020, 09:28:03 AM

...
The code is called from a command call, which is where I catch the error and report it. I certainly should have included that in the original post.
Code - C#: [Select]
  1. [CommandMethod("IR_AssignDripFlows")]
  2. public static void AssignDripFlowsToBlocks()
  3. {
  4.   try
  5.   {
  6.     DripFactory.AssignDripFlows();
  7.   }
  8.   catch (Exception e)
  9.   {
  10.     AID_Application.HandleError(e);
  11.   }
  12. }

...

Are you saying that the MakeLayer() method is called in the execution of AssignDripFlow()? If so, and you somehow convinced that the MakeLayer() causes the issue, can you simplifying the code for a test, something like:

Code - C#: [Select]
  1. [CommandMethod("IR_AssignDripFlows")]
  2. public static void AssignDripFlowsToBlocks()
  3. {
  4.   try
  5.   {
  6.     [ClassName].MakeLayer("[LayerName]");
  7.   }
  8.   catch (Exception e)
  9.   {
  10.     AID_Application.HandleError(e);
  11.   }
  12. }

to just make your point that the code still generate the said error (which I do not see why). If this simplified code works OK, as it should be, then, there is more you need to show in the AssignDripFlows().
Title: Re: Chasing down and understanding eInvalidContext..
Post by: Atook on June 02, 2020, 11:35:54 AM
Yes the MakeLayer is called from the AssignDripFlowsToBlocks function. It's also called a bunch of other places in my code. I can't replicate the error at all, I've never seen it on my machine. And I've tried in BricsCAD and AutoCAD.

The stacktrace is what made me think it might be coming from MakeLayer function. I suppose it could be caused by one of the functions calling it, but I'm not sure how I'd even track that. One thing that's consistent is that it's always
Code: [Select]
moduleAutodesk.AutoCAD.DatabaseServices.Transaction
context-lineVoid DeleteUnmanagedObject()
frame
moduleAutodesk.AutoCAD.Runtime.DisposableWrapper
context-lineVoid !DisposableWrapper()

at the top of the stack when I get the error, making me think I'm doing something wrong with objects and transactions. But the code seems to run correctly 99% of the time. I review the transaction code structures, but don't see anything. I'm not passing transactions as parameters or anything like that.

Is there some resource where I can understand the error better, or circumstances where it's thrown?
Title: Re: Chasing down and understanding eInvalidContext..
Post by: Chumplybum on June 02, 2020, 06:34:14 PM
Try removing this block: using (Active.Document.LockDocument())

You do not need to use this if the command is executed within the document context: https://adndevblog.typepad.com/autocad/2012/05/when-to-lock-the-document.html (https://adndevblog.typepad.com/autocad/2012/05/when-to-lock-the-document.html)
Title: Re: Chasing down and understanding eInvalidContext..
Post by: Atook on June 03, 2020, 08:58:40 AM
Try removing this block: using (Active.Document.LockDocument())
...

Thanks for the reply Chumply. The document lock is in the MakeLayer function because it sometimes gets called without a command call, such as opening a drawing, or closing a form. I'm fairly certain the DocumentLock is required then.

I noticed in the link you posted, he mentions the 'document context', perhaps that's part of the eInvalidContext error I'm getting. Do you know if there's a good place to read up on that?
Title: Re: Chasing down and understanding eInvalidContext..
Post by: zero.half on June 03, 2020, 11:40:26 AM
Is there a chance that user loads some other apps that make use of event handlers?
Title: Re: Chasing down and understanding eInvalidContext..
Post by: Chumplybum on June 03, 2020, 06:50:15 PM
Try removing this block: using (Active.Document.LockDocument())
...

Thanks for the reply Chumply. The document lock is in the MakeLayer function because it sometimes gets called without a command call, such as opening a drawing, or closing a form. I'm fairly certain the DocumentLock is required then.

I noticed in the link you posted, he mentions the 'document context', perhaps that's part of the eInvalidContext error I'm getting. Do you know if there's a good place to read up on that?

Hi atook, here is another article on lock document which may help: https://spiderinnet1.typepad.com/blog/2012/05/autocad-net-any-harm-to-lock-document-when-no-need.html

As the exception is 'context' related, i have a feeling it happening somewhere from the transaction level up... although this is a stab in the dark, try starting the transaction thru the database and not thru the document and see if that solves the issue.

Does the exception get thrown for both of the if conditions?

what version of autocad are you running this on?