0

Good Day,

I'm creating a program to do a few calculations and insert a block at a point the user clicks on, then repeats the calc and insert at any other point the user clicks on until the user cancels (See program below).

The program does everything correctly at the first point the user clicks on. Yet the second time through the while loop, after the user clicks on a point, the program causes a fatal error and crashes Autocad without inserting the block, crash error message: “ Internal Error: !dbobji.cpp@8638: eNotOpenForWrite”.

Would someone please go through this and explain to me where my problem is?

Thank you,

using System;
using Autodesk.AutoCAD.Runtime;
using Autodesk.AutoCAD.ApplicationServices;
using Autodesk.AutoCAD.DatabaseServices;
using Autodesk.AutoCAD.Geometry;
using Autodesk.AutoCAD.EditorInput;
using System.Collections.Generic;
using System.Linq;

[assembly: CommandClass(typeof(Level_Arrow.Program))]

namespace Level_Arrow
{
    public class Program
    {
        [CommandMethod("LevelCalc")]
        public static void InsertBLock(string[] args)
        {
            Document acDoc = Application.DocumentManager.MdiActiveDocument;
            Editor ed = acDoc.Editor;
            Database db = acDoc.Database;
            Transaction tr = db.TransactionManager.StartTransaction();

            PromptStringOptions blkN1 = new PromptStringOptions("");
            blkN1.Message = "\nSpecify Block Name: ";
            blkN1.AllowSpaces = true;
            blkN1.DefaultValue = "Level Arrow";
            PromptResult BlkN2 = ed.GetString(blkN1);
            string blkName = BlkN2.StringResult.ToString();

            BlockTable bt = (BlockTable)tr.GetObject(db.BlockTableId, OpenMode.ForRead);
            BlockTableRecord btr = (BlockTableRecord)tr.GetObject(db.CurrentSpaceId, OpenMode.ForWrite);

            if (!bt.Has(blkName))
            {
                ed.WriteMessage("\nBlock not found.");
            }
            else
            {
                BlockTableRecord OrgBtr = (BlockTableRecord)tr.GetObject(bt[blkName], OpenMode.ForRead);

                PromptStringOptions pOptsUnitScale = new PromptStringOptions("");
                pOptsUnitScale.Message = "\nEnter drawing unit scale (m or mm): ";
                pOptsUnitScale.AllowSpaces = false;
                pOptsUnitScale.DefaultValue = "mm";
                PromptResult drgUnitsRes = ed.GetString(pOptsUnitScale);
                string drgUnits = drgUnitsRes.StringResult.ToLower();

                PromptDoubleOptions pOptsDrgScale = new PromptDoubleOptions("");
                pOptsDrgScale.Message = "\nEnter detail scale(1:_): ";
                PromptDoubleResult drgScale = ed.GetDouble(pOptsDrgScale);
                double drgScalem = drgScale.Value / 1000;
                double drgScalemm = drgScale.Value;

                PromptPointOptions ptBL = new PromptPointOptions("");
                ptBL.Message = "\nIndicate baseline: ";
                PromptPointResult ptBaseline = ed.GetPoint(ptBL);
                if (ptBaseline.Status == PromptStatus.Cancel) return;
                double yBase = ptBaseline.Value.Y;

                PromptDoubleOptions levelBL = new PromptDoubleOptions("");
                levelBL.Message = "\nSpecify Baseline level: ";
                PromptDoubleResult blLevel = ed.GetDouble(levelBL);
                double blLevelmm = blLevel.Value * 1000;
                double blLevelm = blLevel.Value;

                BlockReference insblkref;
                PromptPointOptions ptIns;
                Scale3d blkScale;
                AttributeReference ar;

                int a = 1;
                while (a == 1)
                {
                    using (DocumentLock LckDoc = acDoc.LockDocument())
                    {
                        using (tr)
                        {
                            ptIns = new PromptPointOptions("");
                            ptIns.Message = "\nSpecify insertion point: ";
                            PromptPointResult ptInsert = ed.GetPoint(ptIns);
                            if (ptInsert.Status == PromptStatus.Cancel) return;
                            double yInsert = ptInsert.Value.Y;

                            double ptVar = yInsert - yBase;
                            double ptLevel = 0;

                            ObjectId bdId = bt[blkName];
                            Point3d pt = ptInsert.Value;

                            insblkref = new BlockReference(pt, bdId);
                            insblkref.Rotation = 0;

                            if (drgUnits == "mm")
                            {
                                ptLevel = (blLevelmm + ptVar) / 1000;
                                blkScale = new Scale3d(drgScalemm, drgScalemm, drgScalemm);
                                insblkref.ScaleFactors = blkScale;
                            }
                            else if (drgUnits == "m")
                            {
                                ptLevel = blLevelm + ptVar;
                                blkScale = new Scale3d(drgScalem, drgScalem, drgScalem);
                                insblkref.ScaleFactors = blkScale;
                            }
                            double ptDisp = Math.Round(ptLevel, 3);

                            btr.AppendEntity(insblkref);

                            tr.AddNewlyCreatedDBObject(insblkref, true);

                            if (OrgBtr.HasAttributeDefinitions)
                            {
                                foreach (ObjectId id in OrgBtr)
                                {
                                    DBObject obj = tr.GetObject(id, OpenMode.ForRead);
                                    AttributeDefinition ad = obj as AttributeDefinition;

                                    if (ad != null && !ad.Constant)
                                    {
                                        ar = new AttributeReference();
                                        ar.SetAttributeFromBlock(ad, insblkref.BlockTransform);
                                        ar.Position = ad.Position.TransformBy(insblkref.BlockTransform);
                                        if (ad.Justify != AttachmentPoint.BaseLeft)
                                        {
                                            ar.AlignmentPoint.TransformBy(insblkref.BlockTransform);
                                        }
                                        if (ar.IsMTextAttribute)
                                        {
                                            ar.UpdateMTextAttribute();
                                        }
                                        ar.TextString = ptDisp.ToString();

                                        ObjectId arId = insblkref.AttributeCollection.AppendAttribute(ar);
                                        tr.AddNewlyCreatedDBObject(ar, true);
                                        break;
                                    }
                                }
                            }

                            tr.Commit();

                        } //Transaction end
                    } //Lock end

                    insblkref = null; //tried "insblkref.Dispose();" didn't work.

                } //while loop end
            }
        }
    }
}
JohnLBevan
  • 22,735
  • 13
  • 96
  • 178
Cald
  • 201
  • 2
  • 6
  • Have you tried [searching on the error](http://www.bing.com/search?q=AUtocad++Internal+Error%3A+!dbobji.cpp%408638%3A+eNotOpenForWrite&qs=n&form=QBLH&pq=autocad+internal+error%3A+!dbobji.cpp%408638%3A+enotopenforwrite&sc=0-8&sp=-1&sk=&cvid=5b3e42e77be24b4a90c0c5386775be8f)? – Tim Oct 15 '14 at 07:33

1 Answers1

1

I think the issue's this line:

using (tr)

You declared tr earlier, and use it later (on the second loop). Usually you'd put a using statement around the declaration (i.e. replace Transaction tr = db.TransactionManager.StartTransaction(); with using (Transaction tr = db.TransactionManager.StartTransaction()).

The reason is, at the end of the using block, the variable passed in is closed & disposed; meaning you're closing your transaction before you're finished with it.

JohnLBevan
  • 22,735
  • 13
  • 96
  • 178
  • Unless I missed something in OP's code, the while loop will only execute once, so I don't think the problem is with a disposed object (and I would expect it to generate a runtime error by trying to access a disposed object). I could be wrong though :) – Tim Oct 15 '14 at 07:46
  • John is correct. The While loop should be inside the two using loops, otherwise the transaction object (tr) does not exist when the loops starts for the second time. – Alain Oct 15 '14 at 10:10