Author Topic: WblockCloneObjects() causes System.AccessViolationException  (Read 2792 times)

0 Members and 1 Guest are viewing this topic.

autocart

  • Guest
WblockCloneObjects() causes System.AccessViolationException
« on: September 05, 2011, 04:11:08 AM »
Hi forum,

As mentioned in the subject line, the database-method WblockCloneObjects() keeps causing a System.AccessViolationException with me.

I try to copy the contents (entities) from a xref-database into my current space (which is open in the editor). Therefore I create a new DB and readDwgFile the xref-dwg-file in.
The whole thing is in a foreach loop. Unfortunately it is pretty unpredictory when it occurs. Sometimes it comes sooner, sometimes later. Sometimes the code works without any problems.
At the moment I tried to call WblockCloneObjects() outside of any transaction (therefore the somehow many transactions) but it did not help.
(I am sure the general coding-style could be better too. Please forgive :-))

Any ideas? Please help!!! Thx.

Code: [Select]
            try
            {
                foreach (ObjectId oId in blkDefsXref)
                {
                    // Open xrefDb and copy its modelspace entities for each reference to the current space
                    //
                    // Load Xref into sideDb
                    String xrefPath;
                    BlockTableRecord btr;

                    using (Transaction curTr = curDb.TransactionManager.StartTransaction())
                    {
                        btr = curTr.GetObject(oId, OpenMode.ForRead) as BlockTableRecord;
                        xrefPath = btr.PathName;
                        curTr.Commit();
                    }

                    if (xrefPath.Contains(".\\") || !xrefPath.Contains("\\"))
                        xrefPath = HostApplicationServices.Current.FindFile(xrefPath, curDb, FindFileHint.XRefDrawing);
                    Database sourceDb = new Database(false, true);
                    using (sourceDb)
                    {
                        sourceDb.ReadDwgFile(xrefPath, System.IO.FileShare.Read, true, "");

                        ObjectIdCollection idsEntitiesToCopy = new ObjectIdCollection();
                        using (Transaction sourceTr = sourceDb.TransactionManager.StartTransaction())
                        {
                            // Get the entities which are to be cloned:
                            // Open the source block table record Model space for read
                            BlockTable sourceBlkTbl = sourceTr.GetObject(sourceDb.BlockTableId, OpenMode.ForRead) as BlockTable;
                            BlockTableRecord modelspace_source = sourceTr.GetObject(sourceBlkTbl[BlockTableRecord.ModelSpace], OpenMode.ForRead) as BlockTableRecord;
                            //
                            // Make objectIdCollection to be cloned (of entities in source modelspace)
                            foreach (ObjectId tmpOId in modelspace_source)
                            {
                                DBObject tmpO = sourceTr.GetObject(tmpOId, OpenMode.ForRead);
                                // The next line (if ...) filters the entites (typFilter_EntityClass was defined outside of the try-catch)
                                if (tmpO.GetType() == typFilter_EntityClass || tmpO.GetType().IsSubclassOf(typFilter_EntityClass))
                                    idsEntitiesToCopy.Add(tmpOId);
                            }
                            sourceTr.Commit();
                        }

                        // Finally clone the entities for each reference of the current idBlkDef:
                        ObjectIdCollection oids = getReferencesOfBlockInCurrentSpace(btr);
                        foreach (ObjectId xrefId in oids)
                        {
                            using (Transaction curTr = curDb.TransactionManager.StartTransaction())
                            {
                                BlockReference xrefReference = curTr.GetObject(xrefId, OpenMode.ForRead) as BlockReference;
                                // tranlate the entities to be copied so that they will be optically positioned on the same location in the drawing as the entities in the reference
                                refOcsOrigin = xrefReference.Position;
                                refOcsXaxis = xrefReference.GetPlane().GetCoordinateSystem().Xaxis;
                                refOcsYaxis = xrefReference.GetPlane().GetCoordinateSystem().Yaxis;
                                refOcsZaxis = xrefReference.Normal;
                                refRotation = xrefReference.Rotation;
                                refXscale = xrefReference.ScaleFactors.X;

                                using (Transaction sourceTr = sourceDb.TransactionManager.StartTransaction())
                                {
                                    foreach (ObjectId entityOId in idsEntitiesToCopy)
                                    {
                                        Entity ent = sourceTr.GetObject(entityOId, OpenMode.ForWrite) as Entity;
                                        if (refXscale < 0)
                                            ent.TransformBy(Matrix3d.Mirroring(new Plane(sourceDb.Insbase, new Vector3d(0, 1, 0), new Vector3d(0, 0, 1))));
                                        ent.TransformBy(Matrix3d.Rotation(refRotation, new Vector3d(0, 0, 1), sourceDb.Insbase));
                                        ent.TransformBy(Matrix3d.AlignCoordinateSystem(sourceDb.Insbase, new Vector3d(1, 0, 0), new Vector3d(0, 1, 0), new Vector3d(0, 0, 1),
                                            refOcsOrigin, refOcsXaxis, refOcsYaxis, refOcsZaxis));
                                    }
                                    sourceTr.Commit();
                                }
                                curTr.Commit();
                            }

                            sourceDb.WblockCloneObjects(idsEntitiesToCopy, curDb.CurrentSpaceId, new IdMapping(), DuplicateRecordCloning.Replace, false);

                            // undo the translation
                            using (Transaction sourceTr = sourceDb.TransactionManager.StartTransaction())
                            {
                                foreach (ObjectId entityOId in idsEntitiesToCopy)
                                {
                                    Entity ent = sourceTr.GetObject(entityOId, OpenMode.ForWrite) as Entity;
                                    ent.TransformBy(Matrix3d.AlignCoordinateSystem(refOcsOrigin, refOcsXaxis, refOcsYaxis, refOcsZaxis,
                                        sourceDb.Insbase, new Vector3d(1, 0, 0), new Vector3d(0, 1, 0), new Vector3d(0, 0, 1)));
                                    ent.TransformBy(Matrix3d.Rotation(refRotation * -1, new Vector3d(0, 0, 1), sourceDb.Insbase));
                                    if (refXscale < 0)
                                        ent.TransformBy(Matrix3d.Mirroring(new Plane(sourceDb.Insbase, new Vector3d(0, 1, 0), new Vector3d(0, 0, 1))));
                                }
                                sourceTr.Commit();
                            }
                        }
                    }
                }
            }
            catch (System.Exception e)
            {
                ed.WriteMessage("\nError in else (xref):\n" + e.ToString());
                return;
            }

kaefer

  • Guest
Re: WblockCloneObjects() causes System.AccessViolationException
« Reply #1 on: September 05, 2011, 05:28:27 AM »
Any ideas?

There's nothing really conspicuous about your code, except an abundance of transactions, and that it isn't complete, and the way you're doing the transformations, but - no ideas. Sorry.

This seems to work for me:
Code: [Select]
[<CommandMethod "Test">]
let test() =
    let doc = Application.DocumentManager.MdiActiveDocument
    let db = doc.Database
    let ed = doc.Editor
    use tr = db.TransactionManager.StartTransaction()

    let bt = tr.GetObject(db.BlockTableId, OpenMode.ForRead) :?> BlockTable
    for oid in bt do
        let btr = tr.GetObject(oid, OpenMode.ForRead) :?> BlockTableRecord
        if btr.IsFromExternalReference then
       
            let xrefPath =
                HostApplicationServices.Current.FindFile(
                    btr.PathName, db, FindFileHint.XRefDrawing)
            use sourceDb = new Database(false, true)
            sourceDb.ReadDwgFile(xrefPath, System.IO.FileShare.ReadWrite, true, "")

            let idsEntitiesToCopy = new ObjectIdCollection()
            use sourceTr = sourceDb.TransactionManager.StartTransaction()
            let sourceBlkTbl = sourceTr.GetObject(sourceDb.BlockTableId, OpenMode.ForRead) :?> BlockTable
            let modelspace_source = sourceTr.GetObject(sourceBlkTbl.[BlockTableRecord.ModelSpace], OpenMode.ForRead) :?> BlockTableRecord
            for tmpOId in modelspace_source do
                idsEntitiesToCopy.Add tmpOId |> ignore
            sourceTr.Commit()

            let xrefs = btr.GetBlockReferenceIds(true, false)
            for xoid in xrefs do
                let xref = tr.GetObject(xoid, OpenMode.ForRead) :?> BlockReference
                let idMap = new IdMapping()
                sourceDb.WblockCloneObjects(idsEntitiesToCopy, db.CurrentSpaceId, idMap, DuplicateRecordCloning.Replace, false)

                for o in idMap do
                    let idPair = o :?> IdPair
                    if idPair.IsPrimary then
                        let ent = tr.GetObject(idPair.Value, OpenMode.ForWrite) :?> Entity
                        ent.TransformBy xref.BlockTransform
    tr.Commit()



autocart

  • Guest
Re: WblockCloneObjects() causes System.AccessViolationException
« Reply #2 on: September 06, 2011, 03:41:02 AM »
Thx kaefer for the response,

I found out that it happens when the drawings that I want to copy from (in this case the xrefs) are too large. For example when I run the proggi with an xref containing 4 lines it work ok. When I only change the xref to contain 624 lines and run the proggi in the host drawing again then it crashes.

autocart

  • Guest
Re: WblockCloneObjects() causes System.AccessViolationException
« Reply #3 on: September 06, 2011, 04:04:08 AM »
just found out that when I step through the code slowly (breakpoint and then manually) it works again.

But when I press the "continue" button too fast while debugging then it runs into the error again.

Both with 624 elements.

autocart

  • Guest
Re: WblockCloneObjects() causes System.AccessViolationException
« Reply #4 on: September 06, 2011, 05:52:05 AM »
ok, it seems I finally found the solution (after probably a week of delay):

The wrong part in
sourceDb.WblockCloneObjects(idsEntitiesToCopy, curDb.CurrentSpaceId, new IdMapping(), DuplicateRecordCloning.Replace, false);
is that I passed "new IdMapping()" directly to the calling of the method.
I MUST be defined outside first i.e. as

IdMapping idm = new IdMapping();

and then the method MUST be called with the variablename passed to it like this:

sourceDb.WblockCloneObjects(idsEntitiesToCopy, curDb.CurrentSpaceId, idm, DuplicateRecordCloning.Replace, false);

kaefer

  • Guest
Re: WblockCloneObjects() causes System.AccessViolationException
« Reply #5 on: September 06, 2011, 06:42:07 AM »
Code: [Select]
IdMapping idm = new IdMapping();
sourceDb.WblockCloneObjects(idsEntitiesToCopy, curDb.CurrentSpaceId, idm, DuplicateRecordCloning.Replace, false);

Congratulations. What else did you change? Since binding to a local variable doesn't make a difference.here.

Compare the disassembly with binding:
Quote

IL_00f0: newobj instance void [acdbmgd]Autodesk.AutoCAD.DatabaseServices.IdMapping::.ctor()
IL_00f5: stloc.s idMap
...
IL_00ff: ldloc.s idMap
IL_0101: ldc.i4.2
IL_0102: ldc.i4.0
IL_0103: call instance void [acdbmgd]Autodesk.AutoCAD.DatabaseServices.Database::WblockCloneObjects(class [acdbmgd]Autodesk.AutoCAD.DatabaseServices.ObjectIdCollection, valuetype [acdbmgd]Autodesk.AutoCAD.DatabaseServices.ObjectId, class [acdbmgd]Autodesk.AutoCAD.DatabaseServices.IdMapping, valuetype [acdbmgd]Autodesk.AutoCAD.DatabaseServices.DuplicateRecordCloning, bool)

and without:
Quote

IL_01ab: newobj instance void [acdbmgd]Autodesk.AutoCAD.DatabaseServices.IdMapping::.ctor()
IL_01b0: ldc.i4.2
IL_01b1: ldc.i4.0
IL_01b2: call instance void [acdbmgd]Autodesk.AutoCAD.DatabaseServices.Database::WblockCloneObjects(class [acdbmgd]Autodesk.AutoCAD.DatabaseServices.ObjectIdCollection, valuetype [acdbmgd]Autodesk.AutoCAD.DatabaseServices.ObjectId, class [acdbmgd]Autodesk.AutoCAD.DatabaseServices.IdMapping, valuetype [acdbmgd]Autodesk.AutoCAD.DatabaseServices.DuplicateRecordCloning, bool)

Alas, the WblockCloneObjects approach isn't very robust overall. There are lots of things that can go wrong, with or without fatal errors. E.g. filer errors, locked layers in source, nested Xrefs...

Cheers

autocart

  • Guest
Re: WblockCloneObjects() causes System.AccessViolationException
« Reply #6 on: September 06, 2011, 07:03:58 AM »
Quote
What else did you change?
I'm sorry, but I have to disappoint you  :| In the end that's all that it came down to.
Change that one line of code (respectively make it two lines) and it will work :laugh:. Leave it the old way with just the direct passing of "new ..." and it runs into the error :x. (However to see the error you should use rather larger xref samples (maybe 500-1000 entities?). Otherwise it might execute ok. Even with larger xrefs it may not run into the error every time but the chances are better :-P. Today it did error basically every time with me. But once I changed only that one line of code I had no more errors - so far :pissed:.)
« Last Edit: September 06, 2011, 07:08:31 AM by autocart »