Document.CloseAndSave: Attempted to read or write protected memory

Document.CloseAndSave: Attempted to read or write protected memory

Ertqwa
Advocate Advocate
1,413 Views
5 Replies
Message 1 of 6

Document.CloseAndSave: Attempted to read or write protected memory

Ertqwa
Advocate
Advocate

Hello Forum,

 

I have code that throws the following error:

System.AccessViolationException: Attempted to read or write protected memory. This is often an indication that other memory is corrupt.

At Autodesk.AutoCAD.ApplicationServices.Document.CloseInternal()

At Autodesk.AutoCAD.ApplicationServices.Document.CloseAndSave()

 

It only happens with some drawings. 

 

Why would "Document.CloseAndSave()" throw this error? How can I prevent this/check if the document is ok to be closed and saved?

 

using (Document docTarget = CADS.Application.DocumentManager.Open(strTargetDwgPath, false))
{
    try
    {
        if (!docTarget.IsReadOnly)
        {
            using (DocumentLock dlTarget = docTarget.LockDocument())
            {
                using (Transaction transTarget = docTarget.Database.TransactionManager.StartTransaction())
                {
                    btTarget = transTarget.GetObject(docTarget.Database.BlockTableId, OpenMode.ForRead) as BlockTable;
                    foreach (string strDwgPath in strSourceDwgPathList)
                    {
                        ++intIndex;
                        if (btTarget.Has(System.IO.Path.GetFileNameWithoutExtension(strDwgPath)))
                        {
                            using (Database dbInsert = new Database(false, true))
                            {
                                dbInsert.ReadDwgFile(strDwgPath, System.IO.FileShare.Read, true, "");
                                oidInsert = docTarget.Database.Insert(System.IO.Path.GetFileNameWithoutExtension(strDwgPath), dbInsert, true);
                            }
                            btrInsert = transTarget.GetObject(oidInsert, OpenMode.ForWrite) as BlockTableRecord;
                            if (btrInsert.IsDynamicBlock)
                                btrInsert.UpdateAnonymousBlocks();
                            ++intFound;
                        }
                        else
                            ++intNotFound;
                    }
                    btTarget = null;
                    transTarget.Commit();
                }
            }
            // Save changes.
            docTarget.CloseAndSave(strTargetDwgPath); // <<<< ERROR.
            enuResult = DialogResult.OK;
        }
        else
        {
            docTarget.CloseAndDiscard();
            mstrMessage = "Failed: file is readonly or in use.";
            enuResult = DialogResult.Cancel;
        }
    }
    catch (System.Exception Ex)
    {
        MessageBox.Show(Ex.ToString(), "A");
        mstrMessage = "Error redefining blocks in multiple drawings: " + Ex.Message + " (" + System.Reflection.MethodBase.GetCurrentMethod().DeclaringType.Name + "." + System.Reflection.MethodBase.GetCurrentMethod().Name + ").";
        if (docTarget != null) { docTarget.CloseAndDiscard(); }
        enuResult = DialogResult.Cancel;
    }
}

 


0 Likes
1,414 Views
5 Replies
Replies (5)
Message 2 of 6

cdsouba
Explorer
Explorer

I am having the same issue 14 years later. Did you find a good solution for this? I am getting a similar Access Violation error:
The thread tried to read from or write to a virtual address for which it does not have the appropriate access.
Unhandled exception at 0x00007FFFE8D515CC (AeccUiBase.arx) in acad.exe_260515_172915.dmp: 0xC0000005: Access violation reading location 0x000001C487155FF8.


I have a slightly more complex (generalized) open and close, but the bones are the same and I only get this error at seemingly random times.

Message 3 of 6

ActivistInvestor
Mentor
Mentor

Without providing some additional information such as the relevant code and the usage context (modeless dialog?), etc., there really isn't much anyone can do to help.

 

What is certain is that any API that closes a document, must be called from the application context.

 

In the batch processing solutions that I have built over the years, I routinely use the AcadDocument.Save() (COM API) method, followed by the Document's CloseAndDiscard() method, called from the application context.

0 Likes
Message 4 of 6

mediaPHULA
Participant
Participant

Hello @Ertqwa ,

 

This is a classic issue when working with cross-database insertions and transactions in the AutoCAD API. What you are experiencing is a mix of use-after-free and double-free errors at the native memory layer.

Here are the root causes identified in your code, followed by a fully corrected pattern.

 
Root Causes In Your Code
  1. dbInsert is disposed while the transaction still holds references

    You call docTarget.Database.Insert(..., dbInsert, true) then immediately dispose dbInsert via the using block. The Insert() call with true copies objects from dbInsert into the target database, but the open transaction still holds ObjectId references back into dbInsert's native memory. When dbInsert is disposed, AutoCAD's memory manager later reaches into freed memory—a classic use-after-free. This is the primary crash trigger.

  2. CloseAndSave() is called outside the DocumentLock scope

    Your DocumentLock is disposed before CloseAndSave() is called. AutoCAD requires the document to be locked for the full write-to-close sequence. Closing an unlocked document that was previously modified leaves internal state inconsistent.

  3. btrInsert is opened for write across loop iterations

    btrInsert is declared outside the loop and re-assigned each iteration. Holding a for-write handle from a previous iteration while opening another in the same transaction (especially when UpdateAnonymousBlocks() is called) can corrupt the transaction's internal object map.

  4. using(Document ...) conflicts with CloseAndSave

    The outer using block calls Dispose() on the document when it exits, but CloseAndSave() already closed it inside the block. Disposing an already-closed document causes a second internal close attempt on freed native memory (double-free).

Corrected Pattern
// Do NOT wrap Document in a using block.
Document docTarget = CADS.Application.DocumentManager.Open(strTargetDwgPath, false);

try
{
    if (docTarget == null) { return Cancel; }
    if (docTarget.IsReadOnly) { docTarget.CloseAndDiscard(); return Cancel; }

    // Keep temp databases alive until AFTER transaction commits.
    List<Database> tempDatabases = new List<Database>();

    try
    {
        // DocumentLock scope MUST include CloseAndSave.
        using (DocumentLock dlTarget = docTarget.LockDocument())
        {
            using (Transaction transTarget = docTarget.Database.TransactionManager.StartTransaction())
            {
                BlockTable btTarget = transTarget.GetObject(
                    docTarget.Database.BlockTableId, OpenMode.ForRead) as BlockTable;

                foreach (string strDwgPath in strSourceDwgPathList)
                {
                    ++intIndex;
                    string blockName = Path.GetFileNameWithoutExtension(strDwgPath);
                    if (!btTarget.Has(blockName)) { ++intNotFound; continue; }

                    // Create and read source database — do NOT dispose yet.
                    Database dbInsert = new Database(false, true);
                    dbInsert.ReadDwgFile(strDwgPath, FileShare.Read, true, "");
                    tempDatabases.Add(dbInsert);  // Deferred disposal

                    ObjectId oidInsert = docTarget.Database.Insert(blockName, dbInsert, true);

                    // Scope btrInsert locally — not shared across iterations.
                    BlockTableRecord btrInsert = transTarget.GetObject(
                        oidInsert, OpenMode.ForWrite) as BlockTableRecord;
                    
                    if (btrInsert != null && btrInsert.IsDynamicBlock)
                        btrInsert.UpdateAnonymousBlocks();

                    ++intFound;
                }
                transTarget.Commit();
            }
            // Transaction committed. NOW safe to dispose source databases.

            // CloseAndSave inside the lock scope — required.
            docTarget.CloseAndSave(strTargetDwgPath);
            enuResult = DialogResult.OK;
        }
    }
    finally
    {
        // Safely dispose all temporary databases now that the transaction is closed
        foreach (Database db in tempDatabases)
        {
            try { db.Dispose(); } catch { }
        }
        tempDatabases.Clear();
    }
}
catch (Exception ex)
{
    mstrMessage = "Error: " + ex.Message;
    try
    {
        if (docTarget != null && CADS.Application.DocumentManager.Contains(docTarget))
        {
            docTarget.CloseAndDiscard();
        }
    }
    catch { }
    enuResult = DialogResult.Cancel;
}
 
The Four Rules for this Workflow
  1. Never dispose a source Database early: Wait until the transaction that consumed it has committed and disposed. Defer disposal to a finally block.

  2. Locking scope: CloseAndSave() and CloseAndDiscard() must be called inside the DocumentLock scope.

  3. No using on Documents: Never wrap a Document in a using block if you are calling CloseAndSave() or CloseAndDiscard().

  4. Local scoping: Declare BlockTableRecord and ObjectId variables inside the loop body to prevent stale for-write handles.

Why Only Some Drawings Crash?

Drawings with dynamic blocks, nested XRefs, or large block definition tables are more likely to trigger the use-after-free when dbInsert is disposed prematurely because AutoCAD's background geometry engine has more deferred work on those objects. Simple drawings might silently succeed with the broken pattern, making the bug appear intermittent.

Hope this helps stabilize your add-in!

 

 

mediaPHULA_0-1779013518815.png

Lubhata - Software & Innovations
[email protected]

0 Likes
Message 5 of 6

ActivistInvestor
Mentor
Mentor

My advice to you is to find a more intelligent AI to copy and paste answers from, because pretty much everything in the AI-generated answer that you pasted into your reply is incorrect. The OP probably doesn't need anyone's help using AI to get  Inaccurate/incorrect answers.

Message 6 of 6

cdsouba
Explorer
Explorer

The application runs as a plugin to an open instance of Civil 3D meaning there is always an accessible active document. To make changes to drawings, they are being opened as side databases or fully opened depending on the necessity for making particular changes and the UI window is closed. The current flow for opening drawings is to create a scoped DwgOpenContext and set the saveOnDispose and FullOpenIfClosed (open the drawing fully rather that side database) with the save happening on disposal/end of scope.

Class used for opening (not including all methods for brevity):

Constructor:

        private DwgOpenContext(
            string filePath,
            Document document,
            DwgOpenKind kind,
            bool ownsDocument,
            Document previousActiveDocument,
            DwgOpenOptions options)
        {
            if (document == null) throw new ArgumentNullException(nameof(document));
            if (options == null) options = new DwgOpenOptions();

            _filePath = filePath;
            _ownsDocument = ownsDocument;
            _ownsDatabase = false;
            _saveVersion = options.SaveVersion;
            _previousActiveDocument = previousActiveDocument;
            _auditBeforeOwnedDocumentClose = options.AuditBeforeOwnedDocumentClose;

            Document = document;
            Database = document.Database;
            Kind = kind;

            SaveOnDispose = options.SaveOnDispose;
            SaveAlreadyOpenDocumentOnDispose = options.SaveAlreadyOpenDocumentOnDispose;

            EnterScope(options);
        }

Open:

        public static DwgOpenContext Open(
            string filePath,
            bool fullOpenIfClosed,
            bool saveOnDispose)
        {
            FileOpenMode openMode = saveOnDispose
                ? FileOpenMode.OpenForReadAndWriteNoShare
                : FileOpenMode.OpenForReadAndAllShare;
            DwgOpenOptions options = new DwgOpenOptions
            {
                FullOpenIfClosed = fullOpenIfClosed,
                SaveOnDispose = saveOnDispose,
                SideDatabaseOpenMode = openMode
            };

            return Open(filePath, options);
        }

        public static DwgOpenContext Open(string filePath, DwgOpenOptions options)
        {
            if (string.IsNullOrWhiteSpace(filePath))
                throw new ArgumentException("Drawing path is required.", nameof(filePath));

            if (options == null)
                options = new DwgOpenOptions();

            string normalizedPath = PathHelper.Normalize(filePath);

            Document openDoc;
            if (FileManager.TryGetOpenDocument(normalizedPath, out openDoc))
            {
                DwgOpenKind kind =
                    ReferenceEquals(Application.DocumentManager.MdiActiveDocument, openDoc)
                        ? DwgOpenKind.ActiveDocument
                        : DwgOpenKind.AlreadyOpenDocument;

                return new DwgOpenContext(
                    normalizedPath,
                    openDoc,
                    kind,
                    ownsDocument: false,
                    previousActiveDocument: null,
                    options: options);
            }

            if (options.FullOpenIfClosed)
            {
                Document doc = null;
                Document previousActiveDocument = Application.DocumentManager.MdiActiveDocument;

                try
                {
                    doc = Application.DocumentManager.Open(normalizedPath, false);

                    return new DwgOpenContext(
                        normalizedPath,
                        doc,
                        DwgOpenKind.FullDocumentOpenedByContext,
                        ownsDocument: true,
                        previousActiveDocument: previousActiveDocument,
                        options: options);
                }
                catch
                {
                    if (doc != null)
                    {
                        try { doc.CloseAndDiscard(); }
                        catch { /* do not hide original open/lock exception */ }
                    }

                    throw;
                }
            }

            Database db = null;

            try
            {
                db = new Database(false, true);

                db.ReadDwgFile(
                    normalizedPath,
                    options.SideDatabaseOpenMode,
                    true,
                    "");

                db.CloseInput(true);

                return new DwgOpenContext(
                    normalizedPath,
                    db,
                    options);
            }
            catch
            {
                if (db != null)
                    db.Dispose();

                throw;
            }
        }

Dispose:

        public void Dispose()
        {
            if (_disposed) return;

            _disposed = true;

            bool closeOwnedDocument = _ownsDocument && Document != null;

            try
            {
                // Save side databases here.
                // For already-open documents, save only if explicitly allowed.
                if (!_ownsDocument && SaveOnDispose)
                {
                    if (!WasAlreadyOpen || SaveAlreadyOpenDocumentOnDispose)
                    {
                        SaveOwnedDocumentDatabase();
                    }
                }

                // Full documents opened by this context are handled below after
                // the document lock and working database scope are released.
            }
            finally
            {
                if (_ownsDatabase && Database != null)
                {
                    Database.Dispose();
                    Database = null;
                }

                if (_documentLock != null)
                {
                    _documentLock.Dispose();
                    _documentLock = null;
                }

                RestoreWorkingDatabase();

                try
                {
                    if (closeOwnedDocument && Document != null)
                    {
                        if (SaveOnDispose && PrepareOwnedDocumentForClose(intendToSave: true, stage: "close-save"))
                        {
                            SaveAndCloseOwnedDocument();
                        }
                        else
                        {
                            PrepareOwnedDocumentForClose(intendToSave: false, stage: "close-discard");
                            RestorePreviousActiveDocumentBeforeClose();
                            LogOwnedDocumentCloseState("before discard close");
                            Logger.Log($"About to CloseAndDiscard owned document: {_filePath}");
                            Document.CloseAndDiscard();
                            Logger.Log($"CloseAndDiscard completed: {_filePath}");
                        }
                    }
                }
                catch (Autodesk.AutoCAD.Runtime.Exception ex)
                {
                    Logger.Log($"AutoCAD exception while closing document '{_filePath}': {ex}");
                }
                catch (Exception ex)
                {
                    Logger.Log($"Exception while closing document '{_filePath}': {ex}");
                }
                finally
                {
                    Document = null;
                }
            }
        }

        private void SaveAndCloseOwnedDocument()
        {
            try
            {
                RestorePreviousActiveDocumentBeforeClose();
                LogOwnedDocumentCloseState("before CloseAndSave");
                Logger.Log($"About to CloseAndSave owned document: {_filePath}");
                Document.CloseAndSave(_filePath);
                Logger.Log($"CloseAndSave completed: {_filePath}");
            }
            catch
            {
                Document.CloseAndDiscard();
            }
        }

        private void SaveOwnedDocumentDatabase()
        {
            Database.SaveAs(
                _filePath,
                true,
                _saveVersion,
                Database.SecurityParameters);
        }

Here is a usage example:

                        using (DwgOpenContext ctx = DwgOpenContext.Open(
                            path,
                            fullOpenIfClosed: false,
                            saveOnDispose: true))
                        {
                            using (Transaction tr = ctx.StartTransaction())
                            {
                                try
                                {
                                    if (XrefUpdater.EraseXref(ctx.Database, tr, this.Name, SafeIO.GetDirectoryName(path)))
                                    {
                                        Logger.Log($"Successfully detached xref '{this.Name}' from '{SafeIO.GetFileNameWithoutExtension(path)}'");
                                        log.Add($"Detached xref '{this.Name}' from '{SafeIO.GetFileNameWithoutExtension(path)}'");
                                    }
                                }
                                catch (Exception ex)
                                {
                                    Logger.Log($"Error detaching xref '{this.Name}' from '{SafeIO.GetFileNameWithoutExtension(path)}': {ex}");
                                    log.Add($"Error detaching xref '{this.Name}' from '{SafeIO.GetFileNameWithoutExtension(path)}': {ex.Message}");
                                    ctx.SuppressSaveOnDispose(); // Don't save changes if there's an error
                                    continue;
                                }
                                tr.Commit();
                            }
                        }

 I apologize for how long this post is, but I would really appreciate some help.

0 Likes